2015 ACM/ICPC EC-Final
A. Boxes and Balls
二分找到最大的不超过$n$的$\frac{x(x+1)}{2}$形式的数即可。
#include <bits/stdc++.h> using namespace std ; typedef long long LL ; void solve () { LL n ; scanf ( "%lld" , &n ) ; LL l = 1 , r = 2e9 ; while ( l < r ) { LL m = l + r + 1 >> 1 ; LL t = m * ( m + 1 ) / 2 ; if ( t <= n ) l = m ; else r = m - 1 ; } printf ( "%lld\n" , l * ( l + 1 ) / 2 ) ; } int main () { int T ; scanf ( "%d" , &T ) ; for ( int i = 1 ; i <= T ; ++ i ) { printf ( "Case #%d: " , i ) ; solve () ; } return 0 ; }
B. Business Cycle
二分答案,然后暴力模拟,如果没有爆负,则说明进入了循环节,后面直接算,注意最后要预留若干轮暴力模拟。
#include <bits/stdc++.h> using namespace std ; typedef long long ll ; int n,i; ll G,P,l,r,mid,ans,a[1111111]; bool check(ll have){ if(have>=G)return 1; ll ret=P; ll old=-1; while(ret){ ll st=have; bool flag=0; for(int i=0;i<n;i++){ have+=a[i]; if(have<0)flag=1,have=0; if(have>=G)return 1; ret--; if(!ret)return 0; } if(flag)continue; if(have<=st)return 0; old=have-st; break; } ll p=ret/n; p-=3; if(p<0)p=0; if(p>(G-have)/old)return 1; have+=p*old; ret-=p*n; if(have>=G)return 1; int i=0; while(ret--){ have=max(0LL,have+a[i]); i++; i%=n; if(have>=G)return 1; } return 0; } void solve(){ scanf("%d%lld%lld",&n,&G,&P); for(i=0;i<n;i++)scanf("%lld",&a[i]); l=0,r=G-1,ans=G; while(l<=r){ if(check(mid=(l+r)>>1))r=(ans=mid)-1;else l=mid+1; } printf("%lld\n",ans); } int main () { int T ; scanf ( "%d" , &T ) ; for ( int i = 1 ; i <= T ; ++ i ) { printf ( "Case #%d: " , i ) ; solve () ; } return 0 ; }
C. Suffixes and Palindromes
根据Manacher算法可以得到$O(n)$对相等和不等关系,然后按照$sa$数组逐个构造。
首先$sa[1]$必然填$'a'$,然后对于$sa[i]$,首先通过$rank$数组判断它能否和$sa[i-1]$相等,如果它可以相等,但是因为不等关系矛盾,那么它只能大于$sa[i-1]$。
如此可以在$O(n)$时间内构造字典序最小的一组解,注意无解的处理。
#include <bits/stdc++.h> using namespace std ; typedef long long ll ; const int N=1000110; int n,m,i,j,r,p,f[N]; char a[N],s[N]; int len[N],e[N],fa[N],g[N],G[N],v[N],nxt[N],ed; int sa[N],rk[N],col[N]; void NIE(){ puts("Wrong calculation!"); } inline void addedge(int x,int y){ v[++ed]=y;nxt[ed]=g[x];g[x]=ed; } inline void addedge2(int x,int y){ v[++ed]=y;nxt[ed]=G[x];G[x]=ed; } int F(int x){return fa[x]==x?x:fa[x]=F(fa[x]);} inline bool makesame(int x,int y){ if(x<0||y>m)return 0; if(x==y)return 1; if(x==0||y==m)return 0; if(x%2!=y%2)return 0; if(x%2==1)return 1; x>>=1; y>>=1; if(F(x)!=F(y))fa[fa[x]]=fa[y]; //printf("same %d %d\n",x,y); return 1; } inline bool makediff(int x,int y){ if(x==y)return 0; if(x<0||y>m)return 1; if(x==0||y==m)return 1; if(x%2!=y%2)return 1; if(x%2==1)return 0; x>>=1; y>>=1; addedge(x,y); addedge(y,x); //printf("diff %d %d\n",x,y); return 1; } inline bool bigger(int x,int y){//suffix[x] > suffix[y]? if(x>n)return 0; if(y>n)return 1; return rk[x]>rk[y]; } void solve(){ scanf("%d",&n); //scanf("%s",a+1); //for(i=1;i<=n;i++)s[i<<1]=a[i],s[i<<1|1]='#'; s[0]='$'; s[1]='#'; s[m=(n+1)<<1]='@'; /*for(r=p=0,f[1]=1,i=2;i<m;i++){ for(f[i]=r>i?min(r-i,f[p*2-i]):1;s[i-f[i]]==s[i+f[i]];f[i]++); if(i+f[i]>r)r=i+f[i],p=i; } for(i=1;i<=m;i++)putchar(s[i]);puts(""); for(i=1;i<=m;i++)printf("%d",f[i]);puts(""); */ for(i=1;i<=n;i++)scanf("%d",&sa[i]),sa[i]++; for(i=1;i<=n*2-1;i++){ scanf("%d",&len[i]); } for(i=1;i<=n*2-1;i++){ if(i&1){ if(len[i]%2==0){ NIE(); return; } }else{ if(len[i]%2){ NIE(); return; } } e[i+1]=len[i]+1; } e[1]=1; e[m-1]=e[m]=1; for(i=1;i<=n;i++)fa[i]=i; for(ed=0,i=1;i<=n;i++)g[i]=G[i]=0; for(r=p=0,f[1]=1,i=2;i<m;i++){ for(f[i]=r>i?min(r-i,f[p*2-i]):1;f[i]<e[i];f[i]++){ int x=i-f[i],y=i+f[i]; if(!makesame(x,y)){ NIE(); return; } } if(f[i]!=e[i]){ NIE(); return; } int x=i-f[i],y=i+f[i]; if(!makediff(x,y)){ NIE(); return; } if(i+f[i]>r)r=i+f[i],p=i; } for(i=1;i<=n;i++)F(i); for(i=1;i<=n;i++)for(j=g[i];j;j=nxt[j]){ if(fa[i]==fa[v[j]]){ NIE(); return; } addedge2(fa[i],fa[v[j]]); } for(i=1;i<=n;i++)rk[sa[i]]=i; for(i=1;i<=n;i++)col[i]=0; col[fa[sa[1]]]=1; for(i=2;i<=n;i++){ int x=sa[i]; bool cansame=1; if(bigger(sa[i-1]+1,x+1))cansame=0; //printf("%d %d\n",i,cansame); int pre=col[fa[sa[i-1]]]; if(col[fa[x]]){ if(cansame){ if(col[fa[x]]<pre){ NIE(); return; } }else{ if(col[fa[x]]<=pre){ NIE(); return; } } continue; } for(j=G[fa[x]];j;j=nxt[j])if(col[v[j]]==pre)cansame=0; if(!cansame)pre++; if(pre>26){ NIE(); return; } col[fa[x]]=pre; } for(i=1;i<=n;i++)putchar('a'+col[fa[i]]-1); puts(""); } int main () { int T ; scanf ( "%d" , &T ) ; for ( int i = 1 ; i <= T ; ++ i ) { printf ( "Case #%d: " , i ) ; solve () ; } return 0 ; }
D. Change
分类讨论即可。
#include <bits/stdc++.h> using namespace std ; void solve () { double x , y ; scanf ( "%lf%lf" , &x , &y ) ; int a = x * 100 + 0.5 ; int b = y * 100 + 0.5 ; if ( b == 1 || b == 10 || b == 100 || b == 1000 || b == 10000 ) { if ( a == 2 * b ) printf ( "0.01\n" ) ; else printf ( "0.02\n" ) ; } else printf ( "0.01\n" ) ; } int main () { int T ; scanf ( "%d" , &T ) ; for ( int i = 1 ; i <= T ; ++ i ) { printf ( "Case #%d: " , i ) ; solve () ; } return 0 ; }
E. Colorful Floor
留坑。
F. Hungry Game of Ants
DP出每只蚂蚁往左往右吃完的方案数,用前缀和优化即可。时间复杂度$O(n)$。
#include <bits/stdc++.h> using namespace std ; typedef long long LL ; const int MAXN = 1000005 ; const int mod = 1e9 + 7 ; LL dp[MAXN] , f[MAXN] , sum[MAXN] ; int n , k ; void up ( LL& x , LL y ) { x += y ; if ( x >= mod ) x -= mod ; } void solve () { scanf ( "%d%d" , &n , &k ) ; if ( k == 1 ) { printf ( "0\n" ) ; return ; } for ( int i = 0 ; i <= n ; ++ i ) { dp[i] = f[i] = 0 ; if ( i ) sum[i] = sum[i - 1] + i ; } LL ans = 2 , p = 1 ; for ( int i = 2 ; i < k ; ++ i ) { up ( p , p ) ; if ( sum[i] < sum[k] - sum[i] ) up ( ans , p ) ; } if ( k == n ) { printf ( "%lld\n" , ans * 2 % mod ) ; return ; } dp[k] = f[k] = ans ; for ( int i = 1 , j = 1 ; i <= n ; ++ i ) { while ( sum[i] > 2 * sum[j] ) ++ j ; up ( dp[i] , ( f[i - 1] - f[j - 1] + mod ) % mod ) ; up ( f[i] , ( f[i - 1] + dp[i] ) % mod ) ; } printf ( "%lld\n" , dp[n] ) ; } int main () { int T ; scanf ( "%d" , &T ) ; for ( int i = 1 ; i <= T ; ++ i ) { printf ( "Case #%d: " , i ) ; solve () ; } return 0 ; }
G. Legacy of the Void
留坑。
H. Open Face Chinese Poker
留坑。
I. Champions League
留坑。
J. Dome and Steles
二分答案,然后解出每个砖块能放的范围,按范围从大到小放,每次贪心放大的那一侧即可。
#include <bits/stdc++.h> using namespace std ; typedef long long LL ; const int Maxn = 100005 ; const double eps=1e-10; double a[Maxn],ned[Maxn]; int n; double sqr(double x){return x*x;} int dcmp(double x){if(fabs(x)<eps)return 0;if(x>eps)return 1;return -1;} bool check(double md){ for(int i=0;i<n;i++){ if(dcmp(md*md-a[i])<=0)return 0; } //for(int i=0;i<n;i++)printf("%.3f ",ned[i]);puts(""); double l=md,r=md; for(int i=0;i<n;i++){ if(l<r)swap(l,r); l=min(l,sqrt(md*md-a[i])); if(dcmp(l-1)>=0){l-=1;continue;} else{ if(dcmp(l+r-1)<0)return 0; if((1-l)*(1-l)+a[i]>md*md)return 0; if(i!=(n-1))return 0; } } return 1; } void solve () { scanf("%d",&n); for(int i=0;i<n;i++){ double x,y; scanf("%lf%lf",&x,&y); a[i]=min(sqr(x)+sqr(y/2),sqr(y)+sqr(x/2)); } sort(a,a+n); double l=0,r=1e6; for(int i=0;i<200;i++){ double md=(l+r)/2; if(check(md))r=md; else l=md; } printf("%.12f\n",r); } int main () { int T ; scanf ( "%d" , &T ) ; for ( int i = 1 ; i <= T ; ++ i ) { printf ( "Case #%d: " , i ) ; solve () ; } return 0 ; }
K. Convex Polyhedron
求出三维凸包之后,假如知道了投影方向向量,那么根据叉积的正负号贪心把所有正的法向量加起来即可。于是随机枚举10000个方向向量即可。
#include <bits/stdc++.h> using namespace std ; #define PR 1e-8 #define N 620 struct TPoint { double x , y , z ; TPoint () {} TPoint ( double x , double y , double z ) : x ( x ) , y ( y ) , z ( z ) {} TPoint operator+(const TPoint p){return TPoint(x+p.x,y+p.y,z+p.z);} TPoint operator-(const TPoint p){return TPoint(x-p.x,y-p.y,z-p.z);} TPoint operator*(const TPoint p){ return TPoint(y*p.z-z*p.y,z*p.x-x*p.z,x*p.y-y*p.x); } TPoint operator*(double p){return TPoint(x*p,y*p,z*p);} TPoint operator/(double p){return TPoint(x/p,y/p,z/p);} double operator^(const TPoint p){return x * p.x + y * p.y + z * p.z ;} void show (){ printf ( "%.2f %.2f %.2f\n" , x , y , z ) ; } }center; struct fac{ int a , b , c ; bool ok ; } ; struct T3dhull{ int n ; TPoint ply[N]; int trianglecnt; fac tri[N] ; int vis[N][N]; double dist(TPoint a ) { return sqrt ( a.x * a.x + a.y * a.y + a.z * a.z ) ; } double area( TPoint a , TPoint b , TPoint c ) { return dist ( ( b - a ) * ( c - a ) ) ; } TPoint fa(TPoint a,TPoint b,TPoint c){ return (b-a)*(c-a); } double volume ( TPoint a , TPoint b , TPoint c , TPoint d ) { return ( b - a ) * ( c - a ) ^ ( d - a ) ; } double ptoplane ( TPoint &p , fac& f ) { TPoint m = ply[f.b]-ply[f.a],n=ply[f.c]-ply[f.a],t=p-ply[f.a]; return (m*n)^t; } void deal(int p,int a,int b ){ int f = vis[a][b]; fac add; if(tri[f].ok){ if((ptoplane(ply[p],tri[f]))>PR)dfs(p,f);else{ add.a=b,add.b=a,add.c=p,add.ok=1; vis[p][b]=vis[a][p]=vis[b][a]=trianglecnt; tri[trianglecnt++]=add; } } } void dfs(int p,int cnt ) { tri[cnt].ok = 0 ; deal(p,tri[cnt].b,tri[cnt].a); deal(p,tri[cnt].c,tri[cnt].b); deal(p,tri[cnt].a,tri[cnt].c); } bool same ( int s , int e ) { TPoint a = ply[tri[s].a],b=ply[tri[s].b],c=ply[tri[s].c]; return fabs ( volume(a , b , c , ply[tri[e].a] ) ) < PR && fabs( volume(a , b , c , ply[tri[e].b]))<PR && fabs( volume(a , b ,c , ply[tri[e].c]))<PR; } void construct(){ int i,j; trianglecnt=0; if ( n < 4 ) return ; bool tmp = 1 ; for ( i = 1 ; i < n ; ++ i ) { if ( ( dist(ply[0]-ply[i]))>PR){ swap ( ply[1],ply[i]); tmp=0; break ; } } if ( tmp ) return ; tmp = 1 ; for ( i = 2 ; i < n ; ++ i ) { if ( ( dist((ply[0]-ply[1])*(ply[1]-ply[i])))>PR){ swap ( ply[2],ply[i]); tmp=0; break; } } if(tmp)return; tmp=1; for(i=3;i<n;++i){ if(fabs((ply[0]-ply[1])*(ply[1]-ply[2])^(ply[0]-ply[i]))>PR){ swap(ply[3],ply[i]); tmp=0; break; } } if(tmp)return; tmp=1; fac add; for(i=0;i<4;++i){ add.a=(i+1)%4,add.b=(i+2)%4,add.c=(i+3)%4,add.ok=1; if((ptoplane(ply[i],add))>0)swap(add.b,add.c); vis[add.a][add.b]=vis[add.b][add.c]=vis[add.c][add.a]=trianglecnt; tri[trianglecnt++]=add; } for(i=4;i<n;++i){ for(j=0;j<trianglecnt;++j){ if(tri[j].ok&&(ptoplane(ply[i],tri[j]))>PR){ dfs(i,j); break; } } } int cnt = trianglecnt; trianglecnt=0; for(i=0;i<cnt;++i){ if(tri[i].ok){ tri[trianglecnt++]=tri[i]; } } } void show(){ for ( int i = 0 ; i < trianglecnt;++i){ printf ( "------%d------\n" , i ) ; ply[tri[i].a].show () ; ply[tri[i].b].show () ; ply[tri[i].c].show () ; } } double check(TPoint mydi){ TPoint ret=TPoint(.0,.0,.0); for(int i=0;i<trianglecnt;i++){ TPoint cur=fa(ply[tri[i].a],ply[tri[i].b],ply[tri[i].c]); if((mydi^cur)>=0)ret=ret+cur; } return dist(ret); } int getrand(){ int ret=rand()%200; if(rand()%2)ret=-ret; return ret; } void solve(){ double ans=0; for(int i=0;i<10000;i++){ TPoint mydi; mydi.x=getrand(); mydi.y=getrand(); mydi.z=getrand(); ans=max(ans,check(mydi)); } printf("%.12f\n",ans/2); } } a; void solve () { scanf ( "%d",&a.n); for ( int i = 0 ; i < a.n ; ++ i ) { scanf ( "%lf%lf%lf" , &a.ply[i].x,&a.ply[i].y,&a.ply[i].z); } a.construct(); //a.show(); a.solve(); } int main () { int T ; scanf ( "%d" , &T ) ; for ( int i = 1 ; i <= T ; ++ i ) { printf ( "Case #%d: " , i ) ; solve () ; } return 0 ; }
L. Multiplication Table
枚举约数然后检验。
#include <bits/stdc++.h> using namespace std ; typedef long long LL ; int n,m; int a[1020][1020]; struct Node{ int x,y,z; Node(){} Node(int x,int y,int z):x(x),y(y),z(z){} }nd[1000020]; int cnt; int csx,csy; bool check(int ox,int oy,int rx,int ry){ if(rx-(ox-1)<1)return 0; if(ry-(oy-1)<1)return 0; for(int i=0;i<cnt;i++){ int x=nd[i].x,y=nd[i].y,z=nd[i].z; if(1LL*(rx+(x-ox))*(ry+(y-oy))!=z)return 0; } return 1; } void solve () { scanf("%d%d",&n,&m); cnt=0; for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ char s[20]; scanf("%s",s); if(s[0]=='?')continue; int x=0; for(int k=0;s[k];k++){ x=x*10+s[k]-'0'; } nd[cnt++]=Node(i,j,x); } } if(!cnt){puts("Yes");return;} bool flag=0; int num=nd[0].z; csx=nd[0].x,csy=nd[0].y; for(int i=1;i<cnt;i++){ if(num>nd[i].z){ num=nd[i].z; csx=nd[i].x; csy=nd[i].y; } } for(int i=1;i<=num/i&&!flag;i++){ if(num%i==0){ if(check(csx,csy,i,num/i)){flag=1;break;} if(i*i!=num&&check(csx,csy,num/i,i)){flag=1;break;} } } puts(flag?"Yes":"No"); } int main () { int T ; scanf ( "%d" , &T ) ; for ( int i = 1 ; i <= T ; ++ i ) { printf ( "Case #%d: " , i ) ; solve () ; } return 0 ; }
M. November 11th
对于长度为$n$的长条,对最小值的贡献为$\lfloor\frac{n+1}{2}\rfloor$,对最大值的贡献为$\lfloor\frac{n+2}{3}\rfloor$。
#include <bits/stdc++.h> using namespace std ; typedef long long LL ; int n,m,i,j,k,x,y,f[1111][1111],ans0,ans1; inline int F(int n){ return (n+1)/2; } inline int G(int n){ if(n==1)return 1; return (n+2)/3; } void solve () { scanf("%d%d",&n,&m); for(i=1;i<=n;i++)for(j=1;j<=m;j++)f[i][j]=1; scanf("%d",&k); while(k--)scanf("%d%d",&x,&y),f[x+1][y+1]=0; ans0=ans1=0; for(i=1;i<=n;i++){ j=1; for(;j<=m;){ if(!f[i][j]){j++;continue;} for(k=j;k<=m&&f[i][k];k++); ans0+=F(k-j); ans1+=G(k-j); j=k; } } printf("%d %d\n",ans0,ans1); } int main () { int T ; scanf ( "%d" , &T ) ; for ( int i = 1 ; i <= T ; ++ i ) { printf ( "Case #%d: " , i ) ; solve () ; } return 0 ; }
总结:
- B题忘记考虑预留的情况,导致WA5发。
- C题对无解的判断不足以及数组开少一半,导致WA。
- M题没有想清楚,导致WA。