2016-2017 ACM-ICPC, NEERC, Northern Subregional Contest
A. Anniversary Cake
随便挑两个点切掉就好了。
#include<bits/stdc++.h> using namespace std; const int Maxn=200020; typedef long long LL; typedef pair<int,int>pi; pi a[2]; int main() { freopen("anniversary.in","r",stdin); freopen("anniversary.out","w",stdout); int w,h; scanf("%d%d",&w,&h); int x,y; for(int i=0;i<2;i++){ scanf("%d%d",&x,&y); a[i]=pi(x,y); } sort(a,a+2); //puts("ok"); if(a[0].first==a[1].first){ printf("%d %d %d %d\n",0,a[0].second,w,a[1].second); } else{ printf("%d %d %d %d\n",a[0].first,0,a[1].first,h); } return 0; }
B. Boys and Girls
分类讨论构造。
#include<bits/stdc++.h> using namespace std; const int Maxn=105; typedef long long LL; typedef pair<int,int>pi; int n,x,y; string rep; void pt(int cnt,string s){ for(int i=0;i<cnt;i++){ for(int j=0;j<s.size();j++)rep.push_back(s[j]); } } bool solve(){ int t1=x+y-n,t2=x-t1,t3=y-t1; if(t1<0||t2<0||t3<0)return 0; if(!x&&!y)return 0; if((n==x&&y==0)||(n==y&&x==0)){ if(n==y){ for(int i=0;i<n;i++)pt(1,"G");//putchar('G'); } else{for(int i=0;i<n;i++)pt(1,"B");}//putchar('B');} return 1; } if(t1%2)return 0; if(t1>=4){ if(t1%4==0){ int cnt=t1/4; pt(cnt-1,"BBGG"); pt(2+t2,"B"); pt(2+t3,"G"); return 1; } else{ if(!t2&&!t3)return 0; if(t2){ pt(t2+1,"B"); pt(t3+2,"G"); for(int i=0;i<t1/2-2;i++){ if(i%2==0)pt(2,"B"); else pt(2,"G"); } pt(1,"G"); } else{ pt(t3+1,"G"); pt(t2+2,"B"); for(int i=0;i<t1/2-2;i++){ if(i%2==0)pt(2,"G"); else pt(2,"B"); } pt(1,"B"); } return 1; } } else if(t1==2){ if(t2==t3)return 0; if(t2>=t3){ pt(t2-t3+1,"B"); pt(1,"G"); pt(t3,"BG"); } else{ pt(t3-t2+1,"G"); pt(1,"B"); pt(t2,"GB"); } return 1; } else if(!t1){ if(t2!=t3)return 0; if(t2+t3!=n)return 0; for(int i=0;i<n;i++)pt(1,i&1?"B":"G");//putchar(i&1?'B':'G'); return 1; } } bool go(){ int t1=0,t2=0; for(int i=0;i<rep.size();i++){ int nxt=(i+1)%rep.size(); int bef=(i-1+rep.size())%rep.size(); if(rep[nxt]=='B'||rep[bef]=='B')t1++; if(rep[nxt]=='G'||rep[bef]=='G')t2++; } if(rep.size()!=n)return 0; if(t1!=x||t2!=y)return 0; return 1; } bool check(){ if(rep==""){ string tmp(n,'B'); for(int mask=0;mask<1<<n;mask++){ for(int j=0;j<n;j++){ if(mask>>j&1)tmp[j]='G'; else tmp[j]='B'; } if(go()){ puts("wa"); cout<<tmp<<endl; return 0; } } return 1; } return go(); } int main() { freopen("boysgirls.in","r",stdin); freopen("boysgirls.out","w",stdout); srand(time(NULL)); /* int _=1000; while(_--){ n=16; x=rand()%n,y=rand()%n; */ scanf("%d%d%d",&n,&x,&y); rep=""; if(!solve())puts("Impossible"); else { //printf("%d %d %d\n",n,x,y); //cout<<"myans="<<endl; cout<<rep<<endl; } /* if(!check()){ puts("wa"); while(1); } else puts("ok"); */ //} return 0; }
C. CodeCoder vs TopForces
将所有人按两种rating分开排序,相邻的之间连有向边,那么SCC缩点之后,剩下的图是个竞赛图,求出拓扑序之后前面所有的点都是可达的。
#include<cstdio> #include<algorithm> using namespace std; const int N=500000; int n,i,a[N],b[N],q[N]; int g[2][N],v[2][N],nxt[2][N],ed; int vis[N],size[N],f[N],ans[N],sum,t; inline bool cmpa(int x,int y){return a[x]<a[y];} inline bool cmpb(int x,int y){return b[x]<b[y];} inline void add(int x,int y){ v[0][++ed]=y;nxt[0][ed]=g[0][x];g[0][x]=ed; v[1][ed]=x;nxt[1][ed]=g[1][y];g[1][y]=ed; } void dfs1(int x){ vis[x]=1; for(int i=g[0][x];i;i=nxt[0][i])if(!vis[v[0][i]])dfs1(v[0][i]); q[++t]=x; } void dfs2(int x,int y){ vis[x]=0;f[x]=y;size[y]++; ans[x]=sum; for(int i=g[1][x];i;i=nxt[1][i])if(vis[v[1][i]])dfs2(v[1][i],y); } int main(){ freopen("codecoder.in","r",stdin); freopen("codecoder.out","w",stdout); scanf("%d",&n); for(i=1;i<=n;i++)scanf("%d%d",&a[i],&b[i]); for(i=1;i<=n;i++)q[i]=i; sort(q+1,q+n+1,cmpa); for(i=1;i<n;i++)add(q[i],q[i+1]); sort(q+1,q+n+1,cmpb); for(i=1;i<n;i++)add(q[i],q[i+1]); for(i=1;i<=n;i++)if(!vis[i])dfs1(i); for(i=t;i;i--)if(vis[q[i]]){ dfs2(q[i],q[i]); sum+=size[q[i]]; } for(i=1;i<=n;i++)printf("%d\n",ans[i]+size[f[i]]-1); return 0; }
D. Digital Addition
从低位到高位DP即可。
#include<bits/stdc++.h> using namespace std; const int Maxn=105; typedef long long LL; typedef pair<int,int>pi; pi a[2]; int occ[3][10]={{3,0,2,0,1,1,3,0,3,1}, {5,0,7,7,2,7,7,1,7,7} ,{3,3,1,3,3,2,2,3,3,3} }; int has[10][10][10][3]; int s1[10][Maxn],s2[10][Maxn]; int n; int num[Maxn][3]; int dp[Maxn][2][16]; int rep[3][Maxn]; bool debug; struct Node{ int a,b,c; Node(){} Node(int a,int b,int c):a(a),b(b),c(c){} }pre[Maxn][2][16],pe[Maxn][2][16]; int main() { freopen("digital.in","r",stdin); freopen("digital.out","w",stdout); for(int i=0;i<10;i++) for(int j=0;j<10;j++) for(int k=0;k<10;k++){ for(int ty=0;ty<3;ty++){ has[i][j][k][ty]=occ[ty][i]|(occ[ty][j]<<1)|(occ[ty][k]<<2); } } while(scanf("%d",&n)!=EOF){ int t1=0,t2=0; for(int i=0;i<9;i++){ if(i%2==0){ for(int j=0;j<n;j++){ scanf("%d",&s1[t1][j]); } t1++; } else{ for(int j=0;j<=n;j++){ scanf("%d",&s2[t2][j]); } t2++; } } memset(num,0,sizeof num); for(int i=0;i<n;i++){ for(int j=0;j<4;j++){ num[i][0]|=s2[j][i]<<j; num[i][2]|=s2[j][i+1]<<j; } for(int j=0;j<5;j++){ num[i][1]|=s1[j][i]<<j; } } //for(int i=0;i<n;i++)printf("nm%d %d %d\n",num[i][0],num[i][1],num[i][2]); memset(dp,0,sizeof dp); dp[n][0][0]=1; for(int i=n-1;i>=0;i--) for(int j=0;j<2;j++) for(int mask=0;mask<16;mask++){ if(!dp[i+1][j][mask])continue; //printf("i=%d j=%d mask=%d\n",i,j,mask); for(int a=0;a<10;a++) for(int b=0;b<10;b++) for(int c=0;c<10;c++){ if(i==1&&j==0&&a==7&&b==4&&c==1)debug=1; else debug=0; if((a+b+j)%10!=c)continue; int nj=(a+b+j)/10,nmask=has[a][b][c][0]; int t0=has[a][b][c][0]; int t1=has[a][b][c][1]; int t2=has[a][b][c][2]|mask; //if(debug)printf("t0=%d t1=%d t2=%d\n",t0,t1,t2); if((t1!=num[i][1])||(t2!=num[i][2]))continue; dp[i][nj][nmask]=1; pre[i][nj][nmask]=Node(i+1,j,mask); pe[i][nj][nmask]=Node(a,b,c); //if(debug)printf("i+1=%d %d %d\n",i+1,nj,nmask); } } int cs=num[0][0]; if(!dp[0][0][cs]){puts("NO");} else{ int curj=0,curmask=cs; for(int i=0;i<n;i++){ rep[0][i]=pe[i][curj][curmask].a; rep[1][i]=pe[i][curj][curmask].b; rep[2][i]=pe[i][curj][curmask].c; Node tmp=pre[i][curj][curmask]; curj=tmp.b; curmask=tmp.c; } for(int i=0;i<3;i++){ for(int j=0;j<n;j++)printf("%d",rep[i][j]); puts(""); } } } return 0; }
E. Easy Reading
注意到$n\times m\leq 10^5$,那么有$\min(n,m)\leq\sqrt{10^5}$,不妨假设$n\leq m$,剩下的情况可以通过旋转来保证$n\leq m$。
枚举左端点,往右延伸右端点,维护经过的点的个数,直到找到第一个使得经过的点的个数不小于给定图案里点的个数的右端点为止。
这个过程可以通过two-pointer实现,同时维护目前框住矩形的上下左右边界,以及每一行的hash值,当点数相等、矩形边界相等时,暴力扫描每一行判断是否匹配即可。
时间复杂度$O(|S|\sqrt{nm})$。
#include<cstdio> #include<algorithm> #include<cstring> #include<map> using namespace std; const int N=300010,M=500,Delta=150000; const int S=233,P=1000000007; typedef pair<int,int>PI; typedef long long ll; int len,i,j; int cnt,q[N]; int L,R,U,D,all; map<int,int>cx,cy; map<PI,int>vis; int lazy,invlazy; int val[N]; int st,en; PI que[N]; int ql,qr; char s[N]; int inv,po[N]; int goal[M]; int weight,height,totalX; inline int power(int a,int b){ int t=1; for(;b;b>>=1,a=1LL*a*a%P)if(b&1)t=1LL*t*a%P; return t; } inline char read(){ char x; while((x=getchar())!='.'&&x!='X'); return x=='X'; } void init(){ int n,m; scanf("%d%d",&n,&m); int cnt=n*m; static int h[M]; int L=N,R=-N,U=N,D=-N; if(n<m){ for(int i=1;i<=n;i++){ h[i]=0; for(int j=1;j<=m;j++){ int o=read(); h[i]=(1LL*o*po[j-1]+h[i])%P; if(o==1){ totalX++; L=min(L,j); R=max(R,j); U=min(U,i); D=max(D,i); } } //printf("hash[%d]=%d\n",i,h[i]); } }else{ for(int i=1;i<=len;i++){ if(s[i]=='l')s[i]='u'; else if(s[i]=='r')s[i]='d'; else if(s[i]=='u')s[i]='l'; else if(s[i]=='d')s[i]='r'; } for(int i=1;i<=m;i++)h[i]=0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ int o=read(); h[j]=(1LL*o*po[i-1]+h[j])%P; if(o==1){ totalX++; L=min(L,i); R=max(R,i); U=min(U,j); D=max(D,j); } } } } weight=R-L+1; height=D-U+1; int mul=1; for(int i=1;i<L;i++)mul=1LL*mul*inv%P; //printf("%d %d\n",U,D); for(int i=U;i<=D;i++)goal[i-U+1]=1LL*h[i]*mul%P; } inline bool canmatch(){ /*printf("lazy=%d\n",lazy); for(int i=1;i<=height;i++){ printf("val=%d,goal=%d\n",val[st+i-1],goal[i]); }*/ for(int i=1;i<=height;i++){ if(1LL*val[st+i-1]*lazy%P!=goal[i])return 0; } return 1; } inline void add(char dir){ PI t=que[qr]; if(dir=='l')t.second--; if(dir=='r')t.second++; if(dir=='u')t.first--; if(dir=='d')t.first++; que[++qr]=t; int x=t.first,y=t.second; if(x<U){ val[--st]=0; U--; }else if(x>D){ val[++en]=0; D++; } if(y<L){ lazy=1LL*lazy*S%P; invlazy=1LL*invlazy*inv%P; L--; }else if(y>R){ R++; } cx[x]++; cy[y]++; if(!vis[t]){ val[st+x-U]=(1LL*po[y-L]*invlazy+val[st+x-U])%P; all++; } vis[t]++; } inline void del(){ PI t=que[ql++]; int x=t.first,y=t.second; cx[x]--; cy[y]--; vis[t]--; if(!vis[t]){ val[st+x-U]=(1LL*(P-po[y-L])*invlazy+val[st+x-U])%P; all--; } if(!cx[x]){ if(x==U){ st++; U++; }else if(x==D){ en--; D--; } } if(!cy[y]){ if(y==L){ L++; lazy=1LL*lazy*inv%P; invlazy=1LL*invlazy*S%P; }else if(y==R){ R--; } } } void solve(){ if(height==1&&weight==1){ for(i=1;i<=len;i++)if(s[i]!='l'&&s[i]!='r'&&s[i]!='u'&&s[i]!='d'){ puts("YES"); printf("%d %d\n",i,i); return; } puts("NO"); return; } cnt=0; for(i=1;i<=len;i++) if(s[i]=='l'||s[i]=='r'||s[i]=='u'||s[i]=='d') q[++cnt]=i; if(!cnt){ puts("NO"); return; } L=R=U=D=0; all=1; val[st=en=Delta]=po[0]; cx[0]=1; cy[0]=1; vis[PI(0,0)]=1; lazy=invlazy=1; que[ql=qr=1]=PI(0,0); //now have [1,0] add(s[q[1]]); //now have [1,1] for(i=j=1;i<=cnt;i++){ //[i,j] if(i>1){ del(); } while(j<cnt&&all<totalX){ j++; add(s[q[j]]); } if(all<totalX){ puts("NO"); return; } // printf("%d %d %d\n",q[i],q[j],all); if(all>totalX)continue; if(R-L+1==weight&&D-U+1==height){ //printf("%d %d %d %d %d %d %d\n",q[i],q[j],all,U,D,L,R); //if(q[i]==3&&q[j]==41){ // for(int k=ql;k<=qr;k++)printf("->%d %d\n",que[k].first,que[k].second); //} if(!canmatch())continue; puts("YES"); printf("%d %d\n",q[i],q[j]); return; } } puts("NO"); } int main(){ freopen("easy.in","r",stdin); freopen("easy.out","w",stdout); fgets(s,100,stdin); sscanf(s,"%d",&len); fgets(s+1,100010,stdin); inv=power(S,P-2); for(i=po[0]=1;i<N;i++)po[i]=1LL*po[i-1]*S%P; //printf("po=%d,%d,%d,%d\n",po[0],po[1],po[2],po[3]); init(); //puts("OK"); //printf("%d %d\n",height,weight); solve(); return 0; }
F. Folding
按题意模拟即可。
#include <bits/stdc++.h> using namespace std ; const int INF = 2e9 ; int W , H , w , h ; int get ( int x , int y ) { if ( x == y ) return 0 ; int t = 0 ; while ( x > 2 * y ) x = ( x + 1 ) / 2 , ++ t ; return t + 1 ; } void solve () { int ans = INF ; if ( W >= w && H >= h ) ans = min ( get ( W , w ) + get ( H , h ) , ans ) ; if ( W >= h && H >= w ) ans = min ( get ( W , h ) + get ( H , w ) , ans ) ; if ( ans == INF ) printf ( "-1\n" ) ; else printf ( "%d\n" , ans ) ; } int main () { freopen("folding.in","r",stdin); freopen("folding.out","w",stdout); while ( ~scanf ( "%d%d%d%d" , &W , &H , &w , &h ) ) solve () ; return 0 ; }
G. Gangsters in Central City
将根去掉,那么原树将分裂成若干子树,每个子树如果有强盗,那么就要割掉它们的lca,否则不需要割,线段树维护区间关键点的lca即可。
时间复杂度$O(m\log^2n)$。
#include<cstdio> #include<algorithm> using namespace std; const int N=100010; int n,m,i,x,g[N],v[N],nxt[N],ed; int f[N],d[N],size[N],leaf[N],son[N],top[N]; int st[N],en[N],dfn,from[N]; int ans0,ans1,all; bool vip[N]; char op[5]; int val[262150]; inline void add(int x,int y){v[++ed]=y;nxt[ed]=g[x];g[x]=ed;} void dfs(int x){ size[x]=1; leaf[x]=g[x]==0; for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x]){ f[v[i]]=x; d[v[i]]=d[x]+1; dfs(v[i]); size[x]+=size[v[i]]; leaf[x]+=leaf[v[i]]; if(size[v[i]]>size[son[x]])son[x]=v[i]; } } void dfs2(int x,int y){ st[x]=++dfn; top[x]=y; if(son[x])dfs2(son[x],y); for(int i=g[x];i;i=nxt[i])if(v[i]!=son[x]&&v[i]!=f[x])dfs2(v[i],v[i]); en[x]=dfn; } void dfs3(int x,int y){ from[x]=y; for(int i=g[x];i;i=nxt[i])if(v[i]!=f[x])dfs3(v[i],y); } inline int lca(int x,int y){ for(;top[x]!=top[y];x=f[top[x]])if(d[top[x]]<d[top[y]])swap(x,y); return d[x]<d[y]?x:y; } inline int merge(int x,int y){ if(!x||!y)return x+y; return lca(x,y); } void change(int x,int a,int b,int c,int p){ if(a==b){val[x]=p;return;} int mid=(a+b)>>1; if(c<=mid)change(x<<1,a,mid,c,p);else change(x<<1|1,mid+1,b,c,p); val[x]=merge(val[x<<1],val[x<<1|1]); } int ask(int x,int a,int b,int c,int d){ if(c<=a&&b<=d)return val[x]; int mid=(a+b)>>1,t=0; if(c<=mid)t=ask(x<<1,a,mid,c,d); if(d>mid)t=merge(t,ask(x<<1|1,mid+1,b,c,d)); return t; } inline void ins(int x){ if(vip[x])return; vip[x]=1; all++; int l=st[from[x]],r=en[from[x]]; int z=ask(1,1,n,l,r); if(z)ans0--,ans1-=leaf[z]; change(1,1,n,st[x],x); z=ask(1,1,n,l,r); if(z)ans0++,ans1+=leaf[z]; } inline void del(int x){ if(!vip[x])return; vip[x]=0; all--; int l=st[from[x]],r=en[from[x]]; int z=ask(1,1,n,l,r); if(z)ans0--,ans1-=leaf[z]; change(1,1,n,st[x],0); z=ask(1,1,n,l,r); if(z)ans0++,ans1+=leaf[z]; } int main(){ freopen("gangsters.in","r",stdin); freopen("gangsters.out","w",stdout); scanf("%d%d",&n,&m); for(i=2;i<=n;i++)scanf("%d",&x),add(x,i); dfs(1); dfs2(1,1); for(i=g[1];i;i=nxt[i])dfs3(v[i],v[i]); while(m--){ scanf("%s%d",op,&x); if(op[0]=='+')ins(x); else del(x); printf("%d %d\n",ans0,ans1-all); } return 0; }
H. Hard Cuts
留坑。
I. Integral Polygons
预处理出前缀叉积和,然后将叉积以及点的坐标都模2,枚举右端点,暴力枚举左端点的坐标以及叉积值即可。
时间复杂度$O(n)$。
#include<bits/stdc++.h> using namespace std; const int Maxn=200020; typedef long long LL; int n; int cnt[2][2][2]; int x[Maxn],y[Maxn]; int main() { freopen("integral.in","r",stdin); freopen("integral.out","w",stdout); while(scanf("%d",&n)!=EOF){ int sum=0; for(int i=0;i<n;i++){ scanf("%d%d",x+i,y+i); x[i]%=2;if(x[i]<0)x[i]+=2; y[i]%=2;if(y[i]<0)y[i]+=2; } memset(cnt,0,sizeof cnt); LL ans=0; for(int i=0;i<n;i++){ int nxt=(i+1)%n; sum=(sum+x[nxt]*y[i]-x[i]*y[nxt])%2; if(sum<0)sum+=2; //if(nxt){ for(int j=0;j<2;j++){ for(int k=0;k<2;k++){ for(int bef=0;bef<2;bef++){ int tmpsum=((x[nxt]*j+y[nxt]*k)%2)^(sum^bef); if(tmpsum%2==0){ ans+=cnt[j][k][bef]; } } } //} } //printf("i=%d ans=%lld\n",i,ans); cnt[y[nxt]][x[nxt]][sum]++; } if(sum)puts("0"); else printf("%lld\n",ans-n); } return 0; }
J. Java2016
不断生成随机数取$\max$即可以几乎为$100%$的概率得到$1$,然后可以得到$2,4,8,16,32,64,128$,将$c$二进制拆分即可。
#include<cstdio> int n,i,flag; int main(){ freopen("java2016.in","r",stdin); freopen("java2016.out","w",stdout); scanf("%d",&n); if(!n)return puts("?/?/?"),0; puts("a=? max ?"); for(i=1;i<18;i++)printf("%c=%c max %c\n",'a'+i,'a'+i-1,'a'+i-1); printf("%c=%c/%c\n",'a'+18,'a'+17,'a'+17); for(i=19;i<26;i++)printf("%c=%c+%c\n",'a'+i,'a'+i-1,'a'+i-1); for(i=0;i<8;i++)if(n>>i&1){ if(flag)putchar('+'); flag=1; putchar(i+'a'+18); } return 0; }
K. King’s Heir
按题意模拟即可。
#include<cstdio> #include<algorithm> using namespace std; const int N=500000; int death,n,i,now,ans,id; int s[13]={0,31,28,31,30,31,30,31,31,30,31,30,31}; int get(){ int d,m,y; scanf("%d%d%d",&d,&m,&y); int t=y*365+d+s[m-1]; return t; } int main(){ freopen("king.in","r",stdin); freopen("king.out","w",stdout); for(i=1;i<=12;i++)s[i]+=s[i-1]; death=get(); scanf("%d",&n); ans=-1; id=-1; for(i=1;i<=n;i++){ now=get(); if(death-now>=365*18&&now>ans)ans=now,id=i; } printf("%d",id); return 0; }
总结:
- J题发现题读错之后集体放弃了思考,以致于过了E的情况下还只有9题,下次要注意。