https://www.codechef.com/JAN16?order=desc&sortBy=successful_submissions
这场月赛里除了第四道每题都有磕磕绊绊的感觉。。。展示一下战绩吧:
A.
先做出了4、5的我居然被这题坑了一晚上。。。
递推图画出来很好解释。先把上一层继承下来的东西看作一团雾,然后你发现这一层的新来的数字放开头时刚好和上一层的所有值的开头都乘了一遍(另外还有原来上一层和的数据),哦,然后这团雾就有了定义——上一层的所有值的开头。那么再考虑把这一层的新来的数字放结尾,然后发现和上一层的所有值的结尾都乘了一遍(另外还有原来上一层和的数据)。哦,那么要更新这团雾的定义了——上一层所有值的开头之和+结尾之和。
那么,要怎么计算这一层的和呢?
首先,不考虑所有和这一层新来的值做乘法的运算,那么就会发现,上一层的和在这一层用了两回,所以*2。
又由乘法结合律,即a*b+a*c=a*(b+c),提取公因式(这一层新来的值),剩下的值就是上一层的雾。所以这一层的和=上一层的和*2+这一层新来的值*这团雾。
那么,又怎么计算这团雾?
这就要考你的眼力以及数学归纳法了。假设在第i层,那么第i层的雾就等于上一层的雾+这一层的值*(上一层的个数*2)。这个图画出来还是很好理解的。
ll po[100005]; int main(){ po[0]=1; for(int i=1;i<=100001;++i) po[i]=po[i-1]*2%mod; int t; cin>>t; while(t--){ int n,a; cin>>n; cin>>a; ll s=0,ss=a; for(int i=0;i<n;++i){ cin>>a; s=(s+ss*a%mod)%mod; s=s*2%mod; ss=(ss+po[i]*a%mod)%mod; } cout<<s<<endl; } return 0; }
B.
从一开始的完全没思路到后来有思路却发现是错的(..),想把所有数字中出现的因子都提出来再全都去尝试一遍取最小解,显然光因子的个数就会爆炸了。于是转而想到了用素数,YY一发所有的数都必须要凑到被一个素数刚好整除。
const int MAXN = 10000; unsigned int plist[10000], pcount; unsigned int isPrime[(MAXN >> 5) + 1]; #define setbitzero(a) (isPrime[(a) >> 5] &= (~(1 << ((a) & 31)))) #define setbitone(a) (isPrime[(a) >> 5] |= (1 << ((a) & 31))) #define ISPRIME(a) (isPrime[(a) >> 5] & (1 << ((a) & 31))) void initPrime() { int i, j, m; int t = (MAXN >> 5) + 1; for (i = 0; i < t; ++i) { isPrime[i] = 2863311530; } plist[0] = 2; setbitone(2); setbitzero(1); m = (ll) sqrt((double)MAXN); pcount = 1; for (i = 3; i <= m; i += 2) { if (ISPRIME(i)) { plist[pcount++] = i; for (j = i << 1; j <= MAXN; j += i) { setbitzero(j); } } } if (!(i & 1)) { ++i; } for (; i <= MAXN; i += 2) { if (ISPRIME(i)) { plist[pcount++] = i; } } } int x[10005]; int main(){ initPrime(); int t; sd(t); while(t--){ int n; sd(n); for(int i=0;i<n;++i) sd(x[i]); int p=0; if(x[0]==1) x[0]=2,p++; for(int i=1;i<n;++i){ if(x[i]<x[i-1]){ p+=x[i-1]-x[i]; x[i]=x[i-1]; } } int minn=1000000000; for(int i=0;i<pcount;++i){ int s=0; for(int j=0;j<n;++j){ if(x[j]%plist[i]!=0) s+=plist[i]-x[j]%plist[i]; } minn=min(minn,s); } printf("%d\n",minn+p); } return 0; }
C.
这题也是够坑的,我都用上了凸包(不过超时了),再加上题目意思又搞错(8方向),总之也是脑洞题啊。。。如果不是题目不是格子的话就真该用凸包了。。。
struct Point { int x,y; }; Point p[1000005]; char x[1005][1005]; int main(){ int t; scanf("%d",&t); while(t--){ int nn,m; scanf("%d%d",&nn,&m); int n=0; for(int i=0;i<nn;++i){ scanf("%s",x[i]); int maxx=-10000000,minn=10000000; for(int j=0;j<m;++j){ if(x[i][j]=='*'){ maxx=max(maxx,j); minn=min(minn,j); } } if(maxx==-10000000&&minn==10000000) continue; else if(maxx==minn) p[n].x=i,p[n++].y=maxx; else{ p[n].x=i,p[n++].y=maxx; p[n].x=i,p[n++].y=minn; } } for(int i=0;i<m;++i){ int maxx=-10000000,minn=10000000; for(int j=0;j<nn;++j){ if(x[j][i]=='*'){ maxx=max(maxx,j); minn=min(minn,j); } } if(maxx==-10000000&&minn==10000000) continue; else if(maxx==minn) p[n].x=maxx,p[n++].y=i; else{ p[n].x=maxx,p[n++].y=i; p[n].x=minn,p[n++].y=i; } } int maxx=0; for(int i=0;i<n;++i){ for(int j=i+1;j<n;++j){ int w=max(abs(p[i].x-p[j].x),abs(p[i].y-p[j].y)); maxx=max(maxx,w/2+w%2); } } if(n==0) cout<<0<<endl; else printf("%d\n",maxx+1); } return 0; }
D.
作为第一道AC的题目,表示很简单(贪心,题目绕了点而已)。。。
ll x[100005],y[200005]; int main(){ int t; cin>>t; while(t--){ int n,k,m; cin>>n>>k>>m; for(int i=0;i<n;++i) cin>>x[i]; for(int i=0;i<n;++i) cin>>y[i]; for(int i=0;i<n;++i) x[i]-=y[i]; sort(x,x+n); for(int i=0;i<m+k;++i) cin>>y[i]; m+=k; sort(y,y+m); int p=m-1; ll s=0; for(int i=n-1;i>=0;--i){ if(p<0) s+=x[i]; else{ int u=0; while(p>=0&&y[p]>x[i]){ p--; } if(p>=0) s+=x[i]-y[p]; else s+=x[i]; p--; } } cout<<s<<endl; } return 0; }
E.
容斥原理。代码太长放pastebin里面了。一开始无从下手,图的话画到5就已经很麻烦的情况了,容斥里面不可能出现的情况是硬靠自己YY出来的。。。
http://paste.ubuntu.com/14582010/
F.
dp。然而我一开始觉得并不能dp(因为复杂度太高了),然而几天后发现是我的切入方式不对。。。我表示在比赛的时候花这么长时间去想是万万不能的。
double x[1005][1005],y[1005][1005],dp[1005][1005][4]; int main(){ int n,m; double k; cin>>n>>m>>k; for(int i=1;i<=n;++i){ for(int j=1;j<=m;++j){ cin>>x[i][j]>>y[i][j]; } } for(int i=1;i<=n;++i){ dp[1][i][0]=k/x[i][1]; dp[1][i][3]=k; } double maxx=k; for(int i=2;i<=m;++i){ for(int j=1;j<=n;++j){ dp[i][j][2]=max(dp[i][j][2],dp[i-1][j][0]); //i秒不交易,处于货币状态 dp[i][j][2]=max(dp[i][j][2],dp[i-1][j][2]); dp[i][j][2]=max(dp[i][j][2],dp[i-1][j-1][2]); dp[i][j][2]=max(dp[i][j][2],dp[i-1][j-1][0]); dp[i][j][2]=max(dp[i][j][2],dp[i-1][j+1][2]); dp[i][j][2]=max(dp[i][j][2],dp[i-1][j+1][0]); dp[i][j][3]=max(dp[i][j][3],dp[i-1][j][1]); //i秒不交易,处于商品状态 dp[i][j][3]=max(dp[i][j][3],dp[i-1][j][3]); dp[i][j][3]=max(dp[i][j][3],dp[i-1][j-1][3]); dp[i][j][3]=max(dp[i][j][3],dp[i-1][j-1][1]); dp[i][j][3]=max(dp[i][j][3],dp[i-1][j+1][3]); dp[i][j][3]=max(dp[i][j][3],dp[i-1][j+1][1]); dp[i][j][0]=max(dp[i][j][0],dp[i-1][j][0]); //i秒交易,处于货币状态 dp[i][j][0]=max(dp[i][j][0],dp[i-1][j][1]/x[j][i]); dp[i][j][0]=max(dp[i][j][0],dp[i-1][j][3]/x[j][i]); dp[i][j][1]=max(dp[i][j][1],dp[i-1][j][1]); //i秒交易,处于商品状态 dp[i][j][1]=max(dp[i][j][1],dp[i-1][j][0]*y[j][i]); dp[i][j][1]=max(dp[i][j][1],dp[i-1][j][2]*y[j][i]); maxx=max(maxx,dp[i][j][1]); maxx=max(maxx,dp[i][j][3]); if(maxx>maxxx) break; } if(maxx>maxxx) break; } if(maxx>maxxx) cout<<"Quintillionnaire"<<endl; else printf("%.8f\n",maxx); return 0; }