總結
第五次訓練成績感覺不錯,第一次拿了rank1,在這裏小小的表揚一下自己。但自己也暴露了以下問題:1、在小數浮點邊界處理方面經驗不足,容易犯渾 2、知識廣度儲備還不夠
A - Character Encoding (容斥+組合數學)
description
m個數,每個數取[0…n-1],問總和爲k的方案數(n,m,k<=1e5,T<=400,sum m<=5e6)
solution
若不考慮每個數的範圍限制,很容易知道方案就是,但現在有了範圍限制,我們考慮容斥一下枚舉>=n的數量變成單次詢問O(min(m,k/n))由於所有m加起來<5e6,所以過了
題外話,我之所以會算出這條式子後大眼瞪小眼想要給它降低複雜度10min,是因爲沒看到sum m<=5e6這個條件23333
code
#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
#define fo(i,a,b) for(ll i=a;i<=b;i++)
#define rp(i,a,b) for(ll i=a;i>=b;i--)
using namespace std;
const ll maxn=2e5+5;
const ll mo=998244353;
ll c[maxn],ni[maxn];
ll n,m,i,t,j,k,l,x,y,z,p,T,r,mid,q;
ll ans,ans1;
ll mi(ll x,ll y){
if (y==1) return x;
ll t=mi(x,y/2);
if (y%2) return t*t%mo*x%mo;return t*t%mo;
}
ll dg(ll x,ll y){
return c[x]*ni[y]%mo*ni[x-y]%mo;
}
int main(){
c[0]=1;
fo(i,1,maxn-5)c[i]=c[i-1]*i%mo;
t=maxn-5;ni[t]=mi(c[t],mo-2);
rp(i,t-1,0) ni[i]=ni[i+1]*(i+1)%mo;
scanf("%lld",&T);
while (T--){
scanf("%lld%lld%lld",&n,&m,&k);
if ((ll)(n - 1) * m < k) {
printf("0\n");
continue;
}
ans=0;t=k/n;p=-1;
fo(i,0,t){
p=-p;
ans+=p*dg(m,i)*dg(k-n*i-1+m,m-1)%mo;
}
ans=(ans%mo+mo)%mo;
printf("%lld\n",ans);
}
}
B - Pizza Hub (計算幾何)
description
把一個三角形放到一個指定寬度的矩形內,問矩形的最小高度
solution
歷史的經驗教訓表明,實數在0附近要控制精度如給一個1e-10的範圍
我們發現最小的情況一定可以轉化爲是三角形的一個頂點釘在矩形的一個頂點,另一個頂點釘在矩形的對邊(包含頂點),然後枚舉頂點討論下圖這兩種情況即可
code
#include<bits/stdc++.h>
#define ll long long
#define db double
#define max(a,b) ((a>b)?a:b)
#define min(a,b) ((a<b)?a:b)
#define fo(i,a,b) for(ll i=a;i<=b;i++)
#define rp(i,a,b) for(ll i=a;i>=b;i--)
using namespace std;
const ll maxn=2e5+5;
struct code{
db x,y;
}a[3];
int n,m,i,t,j,k,T;
db l,r,mid,x,y,z,w,b,c,d,p,s,mx,mi;
db sqr(db x){return x*x;}
db dg(int x,int y){return sqrt(sqr(a[x].x-a[y].x)+sqr(a[x].y-a[y].y));}
db dg1(int x,int y){return sqr(a[x].x-a[y].x)+sqr(a[x].y-a[y].y);}
db pan(int x,int y,int z){
db x2=a[y].x-a[x].x,x3=a[z].x-a[x].x,y2=a[y].y-a[x].y,y3=a[z].y-a[x].y;
db c1=(y2*y3+x2*x3)/sqrt(dg1(x,y)*dg1(x,z)),c2=w/dg(x,y);
db s1=sqrt(1-sqr(c1)),s2;
if (c2>1) c2=1,s2=0;
else s2=sqrt(1-sqr(c2));
db c3=c1*c2-s1*s2,s3=c1*s2+c2*s1,t=dg(x,z);
db first,second;
if (c3*t>w+1e-10 || c3*t<-1e-10)first=1e9;
else first=max(s3*t,s2*dg(x,y));
c3=c2*c1+s2*s1;s3=s2*c1-c2*s1;
if (s3<-1e-10 || c3*t>w+1e-10) second=1e9;
else second=s2*dg(x,y);
return min(first,second);
}
int main(){
scanf("%d",&T);
while (T--){
scanf("%lf%lf%lf%lf%lf%lf%lf",&a[0].x,&a[0].y,&a[1].x,&a[1].y,&a[2].x,&a[2].y,&w);
l=min(pan(0,1,2),pan(0,2,1));
l=min(l,min(pan(1,0,2),pan(1,2,0)));
l=min(l,min(pan(2,0,1),pan(2,1,0)));
l+=1e-12;
if (l<1e9)printf("%.9lf\n",l);
else printf("impossible\n");
}
}
J - Taotao Picks Apples (二分)
description
一個長度爲n序列,m個詢問,每次修改1個點的高度(可逆),問修改後的從頭開始取每次取比當前高度更高的數的個數的答案
solution
我們討論兩種情況:1、修改的數本來不在答案序列裏的數,若降低則對答案沒影響,若升高則看看它離他最近的左右兩個數,答案更新一下
2、修改的數本來在答案序列裏,則同樣討論一下就好啦
code
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int t,n,m,p,q,num=0,num2=0;
int h[maxn],maxh[maxn],ans[maxn],b[maxn],maxh2[maxn],o[maxn];
int main(){
cin>>t;
while(t--){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&h[i]);
num=0;num2=0;
for(int i=0;i<=n+5;i++) {
o[i]=0;
maxh[i]=0;
maxh2[i]=0;
ans[i]=0;
}
for(int i=1;i<=n;i++){
if(h[i]>maxh[i-1]){
maxh[i]=h[i];
ans[i]=ans[i-1]+1;
b[++num]=h[i];
maxh2[++num2]=0;
o[num]=num2;
}
else{
maxh[i]=maxh[i-1];
ans[i]=ans[i-1];
if(h[i]>maxh2[num2]) maxh2[++num2]=h[i];
}
}
o[num+1]=num2+1;
/* for(int i=1;i<=num2;i++) printf("%d ",maxh2[i]);
printf("\n");
for(int i=1;i<=num;i++) printf("%d ",b[i],o[i]);
printf("\n");*/
while(m--){
scanf("%d%d",&p,&q);
if(h[p]==q){
printf("%d\n",ans[n]);
}
if(q<h[p]){
if(ans[p]==ans[p-1]) printf("%d\n",ans[n]);
else{
int sum=0;
if(q>maxh[p-1]) sum=ans[p-1]+1;
else sum=ans[p-1];
int tmp=upper_bound(b+1,b+num+1,h[p])-b;
// cout<<tmp<<" ";
int tmp2=upper_bound(maxh2+o[tmp-1],maxh2+o[tmp],max(q,maxh[p-1]))-maxh2;
sum+=num-tmp+1+o[tmp]-tmp2;
printf("%d\n",sum);
}
}
if(q>h[p]){
if(q>maxh[p-1]){
int tmp=upper_bound(b+1,b+num+1,q)-b;
// cout<<tmp<<" ";
printf("%d\n",ans[p-1]+1+num-tmp+1);
}
else printf("%d\n",ans[n]);
}
}
}
return 0;
}