A. Dunai 阅读题,贪心签到
C. Grass 计算几何,暴力,set优化
D. Sternhalma 状压DP,复杂模拟
E. Python Will be Faster than C++ 签到
G. Grade 2 打表
I. Dragon Bloodline 二分查找,优先队列优化,贪心,思维
J. Eat, Sleep, Repeat 博弈论,思维,模拟
C. Grass 不难发现,两点共线,三点共线,四点共线都不影响答案,只有5点共线时没有答案。 现证明选取前四点后,暴力枚举5-->n点若没有答案则无解。因为扫描一遍也无解证明n个点全部共线,故没有答案,否则一定有。
下面就对选中的五个点暴力枚举中心点,这里最好运用向量的知识,若中心点A,指向B的向量覆盖C,就说明共线,对向量(x,y)加入set,先进行gcd的化简,注意这里的gcd化简gcd必须取绝对值,即保留x,y原有正负性不变。
#include <bits/stdc++.h> using namespace std; typedef long long int ll; # define mod 1000000007 ll x[1000000+10],y[1000000+10]; ll xx[100000+10],yy[100000+10]; bool check(int id) { xx[5]=x[id]; yy[5]=y[id]; for(int i=1;i<=5;i++) { set<pair<int,int>>s; for(int j=1;j<=5;j++) { if(i==j) continue; ll nowx=(xx[i]-xx[j]); ll nowy=(yy[i]-yy[j]); ll gcd=__gcd(nowx,nowy); gcd=abs(gcd); nowx/=gcd; nowy/=gcd; s.insert(make_pair(nowx,nowy)); } if(s.size()==4) { cout<<"YES"<<endl; cout<<xx[i]<<" "<<yy[i]<<endl; for(int j=1;j<=5;j++) { if(i==j) continue; cout<<xx[j]<<" "<<yy[j]<<endl; } return 1; } } return 0; } int main() { int t; cin>>t; while(t--) { int n; cin>>n; for(int i=1;i<=n;i++) { cin>>x[i]>>y[i]; } if(n<5) { cout<<"NO"<<endl; continue; } for(int i=1;i<=4;i++) { xx[i]=x[i]; yy[i]=y[i]; } int flag=0; for(int i=5;i<=n;i++) { if(check(i)) { flag=1; break; } } if(!flag) { cout<<"NO"<<endl; continue; } } return 0; }
D. Sternhalma
是个细节不多,但是技巧较多的题
首先考虑用二进制代表19个点存在情况,然后大力DP,但不同于以往二进制DP当前状态更新之后状态的套路,本题还需要对二进制1的个数排个序。这样,倒着推,我们就预处理了每次询问的状态。因为两种操作,消除还是跳过,都会消去跳棋,这就意味着,当前状态只能被比自己棋数少的转移到。
而跳跃,这一过程实现非常复杂。预先存储每个点六个方位的状态,每次枚举跳跃时,有十二种跳跃方式。一个字符也不能错。
#include <bits/stdc++.h> using namespace std; typedef long long int ll; # define mod 1000000007 ll val[50]; int l[50],r[50],lshang[50],rshang[50],lxia[50],rxia[50]; void init() { lshang[0]=-1; rshang[0]=-1; l[0]=-1; r[0]=1; lxia[0]=3; rxia[0]=4; lshang[1]=-1; rshang[1]=-1; l[1]=0; r[1]=2; lxia[1]=4; rxia[1]=5; lshang[2]=-1; rshang[2]=-1; l[2]=1; r[2]=-1; lxia[2]=5; rxia[2]=6; lshang[3]=-1; rshang[3]=0; l[3]=-1; r[3]=4; lxia[3]=7; rxia[3]=8; lshang[4]=0; rshang[4]=1; l[4]=3; r[4]=5; lxia[4]=8; rxia[4]=9; lshang[5]=1; rshang[5]=2; l[5]=4; r[5]=6; lxia[5]=9; rxia[5]=10; lshang[6]=2; rshang[6]=-1; l[6]=5; r[6]=-1; lxia[6]=10; rxia[6]=11; lshang[7]=-1; rshang[7]=3; l[7]=-1; r[7]=8; lxia[7]=-1; rxia[7]=12; lshang[8]=3; rshang[8]=4; l[8]=7; r[8]=9; lxia[8]=12; rxia[8]=13; lshang[9]=4; rshang[9]=5; l[9]=8; r[9]=10; lxia[9]=13; rxia[9]=14; lshang[10]=5; rshang[10]=6; l[10]=9; r[10]=11; lxia[10]=14; rxia[10]=15; lshang[11]=6; rshang[11]=-1; l[11]=10; r[11]=-1; lxia[11]=15; rxia[11]=-1; lshang[12]=7; rshang[12]=8; l[12]=-1; r[12]=13; lxia[12]=-1; rxia[12]=16; lshang[13]=8; rshang[13]=9; l[13]=12; r[13]=14; lxia[13]=16; rxia[13]=17; lshang[14]=9; rshang[14]=10; l[14]=13; r[14]=15; lxia[14]=17; rxia[14]=18; lshang[15]=10; rshang[15]=11; l[15]=14; r[15]=-1; lxia[15]=18; rxia[15]=-1; lshang[16]=12; rshang[16]=13; l[16]=-1; r[16]=17; lxia[16]=-1; rxia[16]=-1; lshang[17]=13; rshang[17]=14; l[17]=16; r[17]=18; lxia[17]=-1; rxia[17]=-1; lshang[18]=14; rshang[18]=15; l[18]=17; r[18]=-1; lxia[18]=-1; rxia[18]=-1; } vector<int>v; bool cmp(int x,int y) { return __builtin_popcount(x)<__builtin_popcount(y); } ll dp[(1<<19)]; bool check(int x,int i) { return ((x&(1<<i))!=0); } int del(int x,int i) { return (x^(1<<i)); } int add(int x,int i) { return (x|(1<<i)); } int cnt[5]; int main() { init(); for(int i=0;i<19;i++) { cin>>val[i]; } for(int i=1;i<(1<<19);i++) { v.push_back(i); dp[i]=-1e9; } sort(v.begin(),v.end(),cmp); dp[0]=0; for(auto i:v) { for(int j=0;j<19;j++) { if(check(i,j)) { dp[i]=max(dp[i],dp[del(i,j)]); //左上跳1 int ls=lshang[j]; if(check(i,ls)&&lshang[ls]!=-1) { int pre=del(i,ls); pre=del(pre,j); pre=add(pre,lshang[ls]); dp[i]=max(dp[i],dp[pre]+val[ls]); // debug(i); } //左上跳2 if(check(i,ls)&&rxia[j]!=-1) { int pre=del(i,j); pre=del(pre,ls); pre=add(pre,rxia[j]); dp[i]=max(dp[i],dp[pre]+val[j]); //debug(i); } //右上跳 int rs=rshang[j]; if(check(i,rs)&&rshang[rs]!=-1) { int pre=del(i,j); pre=del(pre,rs); pre=add(pre,rshang[rs]); dp[i]=max(dp[i],dp[pre]+val[rs]); } if(check(i,rs)&&lxia[j]!=-1) { int pre=del(i,j); pre=del(pre,rs); pre=add(pre,lxia[j]); dp[i]=max(dp[i],dp[pre]+val[j]); } //左上跳1 ls=l[j]; if(check(i,ls)&&l[ls]!=-1) { int pre=del(i,ls); pre=del(pre,j); pre=add(pre,l[ls]); dp[i]=max(dp[i],dp[pre]+val[ls]); } //左上跳2 if(check(i,ls)&&r[j]!=-1) { int pre=del(i,j); pre=del(pre,ls); pre=add(pre,r[j]); dp[i]=max(dp[i],dp[pre]+val[j]); } //右跳 rs=r[j]; if(check(i,rs)&&r[rs]!=-1) { int pre=del(i,j); pre=del(pre,rs); pre=add(pre,r[rs]); dp[i]=max(dp[i],dp[pre]+val[rs]); } if(check(i,rs)&&l[j]!=-1) { int pre=del(i,j); pre=del(pre,rs); pre=add(pre,l[j]); dp[i]=max(dp[i],dp[pre]+val[j]); } //左下跳1 ls=lxia[j]; if(check(i,ls)&&lxia[ls]!=-1) { int pre=del(i,ls); pre=del(pre,j); pre=add(pre,lxia[ls]); dp[i]=max(dp[i],dp[pre]+val[ls]); } //左下跳2 if(check(i,ls)&&rshang[j]!=-1) { int pre=del(i,j); pre=del(pre,ls); pre=add(pre,rshang[j]); dp[i]=max(dp[i],dp[pre]+val[j]); } //右下跳 rs=rxia[j]; if(check(i,rs)&&rxia[rs]!=-1) { int pre=del(i,j); pre=del(pre,rs); pre=add(pre,rxia[rs]); dp[i]=max(dp[i],dp[pre]+val[rs]); } if(check(i,rs)&&lshang[j]!=-1) { int pre=del(i,j); pre=del(pre,rs); pre=add(pre,lshang[j]); dp[i]=max(dp[i],dp[pre]+val[j]); } } } } int m; cin>>m; while(m--) { int now=0; for(int i=0;i<19;i++) { char ch; cin>>ch; if(ch=='#') { now|=(1<<i); } } cout<<dp[now]<<endl; } return 0; }
I. Dragon Bloodline
二分龙蛋数量,把全部龙蛋需要的各个材料数都加进大根堆,我们现在就需要把他们都变成0.考虑大的怼大的,当前材料靠当前bi消灭的时候,如果能整除,那就直接正常分类讨论就行了。一旦不能整除,如果当前材料仅仅需要bi一个的话,直接用bi一定不劣。因为当前堆顶的一定是最大的,而当前bi是一定要用的,无论用在哪个材料,都只是消灭掉一个而已。如果需要多个bi,但是不能整除的话,多出来的那一部分材料不要占用当前bi一次,放进堆里,这样后面小的bi可以冲抵掉他。
#include <bits/stdc++.h> using namespace std; typedef long long int ll; ll a[500000+10],b[500000+10],c[500000+10]; int n,m; bool check(ll mid) { priority_queue<ll>q; for(int i=1;i<=n;i++) { q.push(a[i]*mid); } for(int i=1;i<=m;i++) { c[i]=b[i]; } int nowpos=m; while(!q.empty()) { ll now=q.top(); q.pop(); if(nowpos==0) return 0; ll temp=c[nowpos]; ll nowcnt=max(1ll,now/(1ll<<(nowpos-1))); if(temp>=nowcnt) { c[nowpos]-=nowcnt; now-=nowcnt*(1<<(nowpos-1)); if(now>0) { q.push(now); } if(c[nowpos]==0) nowpos--; } else { ll fuck=c[nowpos]*(1ll<<(nowpos-1)); now-=fuck; if(now>0) { q.push(now); } nowpos--; } } return q.size()==0; } int main() { int t; cin>>t; while(t--) { scanf("%d%d",&n,&m); ll sum=0; for(int i=1;i<=n;i++) { scanf("%lld",&a[i]);sum+=a[i]; } ll fuck=0; for(int i=1;i<=m;i++) { scanf("%lld",&b[i]);fuck+=(b[i]*(1ll<<(i-1))); } ll l=1,r=fuck/sum+10000; while(l<=r) { ll mid=(l+r)>>1; if(check(mid)) { l=mid+1; } else { r=mid-1; } } cout<<r<<'\n'; } return 0; }
J. Eat, Sleep, Repeat
博弈论仅仅是次要的,这题还是一个模拟。
当xi,yi都没有0的时候,那么全部ai都能被消掉,直接看和的奇偶。
当xiyi有0的时候,考虑从小到大消去,把yi为0的作为每段线段的开头,之后x值连续递增1的加入次线段,代表了这段线段是受限线段。受限线段与受限线段直接,是可以任意取值的。
#include <bits/stdc++.h> using namespace std; typedef long long int ll; # define mod 1000000007 int a[100000+10]; struct node { int x,y; }; bool cmp(struct node x, struct node y) { return x.x<y.x; } struct node s[100000+10]; struct duan { int b; vector<pair<int,int>>v; }; struct duan t[100000+10]; unordered_map<int,int>mp; int main() { int tt; cin>>tt; while(tt--) { int n,m; cin>>n>>m; ll sum=0; mp.clear(); for(int i=1; i<=n; i++) { cin>>a[i]; t[i].b=0; t[i].v.clear(); mp[a[i]]++; sum+=a[i]; sum%=2; } int fuck=0; for(int i=1; i<=m; i++) { cin>>s[i].x>>s[i].y; t[i].v.clear(); } if(fuck) { cout<<"FuuFuu"<<endl; continue; } sort(s+1,s+1+m,cmp); sort(a+1,a+1+n); int pre=-1,flag=0; int len=0; for(int i=1; i<=m; i++) { if(s[i].y==0||s[i].x==0) { flag=1; len++; pre=s[i].x; t[len].b=s[i].x; if(s[i].x==0) { t[len].v.push_back(make_pair(s[i].x,s[i].y)); } } else { if(flag) { if(s[i].x==s[i-1].x+1) { flag=1; t[len].v.push_back(make_pair(s[i].x,s[i].y)); } else { flag=0; } } else { flag=0; continue; } } } if(len==0) { if(sum%2==1) { cout<<"Pico"<<endl; } else { cout<<"FuuFuu"<<endl; } } else { sum=0; int nowpos=1; t[0].b=0; for(int i=1; i<=n; i++) { while(nowpos<=len&&t[nowpos].b<a[i]) { nowpos++; } nowpos--; if(nowpos==0) { sum+=a[i]; } else { int nowflag=0; int maxx=t[nowpos].b; int id=0; for(auto it:t[nowpos].v) { maxx=max(maxx,it.first); if(it.first<=a[i]) { if(it.second) { t[nowpos].v[id].second--; nowflag=1; sum+=(a[i]-it.first); break; } } else { break; } id++; } if(nowflag) continue; // cout<<a[i]<<" "<<maxx<<endl; if(maxx>a[i]) while(1); sum+=(a[i]-maxx-1); } } // cout<<sum<<endl; if(sum%2==1) { cout<<"Pico"<<endl; } else { cout<<"FuuFuu"<<endl; } } } return 0; }