[考试反思]1005csp-s模拟测试60:招魂
最近总是好一场烂一场的。没有连续两场排名波动小于20的。。。
没人管。反正大脸一点脸没有就又AK了。
但是T3爆零这种事情吧。。。
爆搜不是很难打,但是想优化想了半天剩的时间不够结果赶忙打出来了,然后就挂了。
合理安排时间,要把码暴力的时间也考虑到,要检查。
T1T2仍然挂上了对拍,所以没有全盘爆炸。
总体来说是相对简单的一套题。
T1:嘟嘟噜
我的思路不太一样。但是复杂度是相似的。
$O(m+log_{\frac{m}{m-1}}\frac{n}{m})$
因为题面里说开无限栈了,然后我就想到了递归打法。
int winner(int n)函数处理剩下n个人时最终胜者的目前编号。
那么如果剩余人数n>m,那么你就可以把n/m个人同时干掉,从第n/m*m+1个人开始数来计算胜者
如果n<=m的话,那就干掉一个人然后继续递归,和约瑟夫一样。
这样的话每次递归,人数会在$-1$和$\times \frac{m-1}{m}$里选一个来 让它快速减小,可以得到上面的复杂度。
据$o0O$打表证明,使n为1e9,当m取到大约1e6时函数的增长速度达到峰值,再之后的增长速度大约为线性。
而本题数据范围1e5时的函数值仅仅是1e5多一点。
总之O(可过)。
1 #include<cstdio> 2 int m,n,t; 3 int winner(int n){ 4 if(n==1)return n; 5 if(n<m)return (winner(n-1)+m-1)%n+1; 6 int N=n/m,w=winner(n-N); 7 if(N*m+w<=n)return N*m+w; 8 w-=n-N*m;return (w-1)/(m-1)*m+(w-1)%(m-1)+1; 9 } 10 main(){ 11 scanf("%d",&t); 12 while(t--)scanf("%d%d",&n,&m),printf("%d\n",winner(n)); 13 }
T2:天才绅士少女助手克里斯蒂娜
这道题是放假前让zkq出数据的那道题(这数据不是现成的嘛,还白给他送了个AC)
化式子,拆平方,最后发现要求的就是$\sum\limits_{i=l}^r \sum\limits_{j=l}^r x_i^2 y_j^2 - x_i y_i x_j y_j$
乘法分配律,得到的就是区间$(\sum\limits_{i=l}^{r}x_i^2)\times(\sum\limits_{i=l}^{r}y_i^2) - (\sum\limits_{i=l}^{r}x_iy_i)^2$
线段树或树状数组维护区间$x^2,y^2,xy$的和即可。注意常数。
1 #include<cstdio> 2 #define mod 20170927 3 int cl[4000005],cr[4000005],x2[4000005],y2[4000005],xy[4000005],x[1000005],y[1000005],XY,X2,Y2; 4 int read(){ 5 register int p=0;register char ch=getchar(); 6 while(ch<'0'||ch>'9')ch=getchar(); 7 while(ch>='0'&&ch<='9')p=(p<<3)+(p<<1)+ch-48,ch=getchar(); 8 return p; 9 } 10 #define lc p<<1 11 #define rc p<<1|1 12 int Mod(int p){return p>=mod?p-mod:p;} 13 void up(int p){x2[p]=Mod(x2[lc]+x2[rc]);y2[p]=Mod(y2[lc]+y2[rc]);xy[p]=Mod(xy[lc]+xy[rc]);} 14 void build(int p,int l,int r){ 15 cl[p]=l;cr[p]=r; 16 if(l==r){long long x=read(),y=read();x2[p]=x*x%mod;y2[p]=y*y%mod;xy[p]=x*y%mod;return;} 17 build(lc,l,l+r>>1);build(rc,(l+r>>1)+1,r);up(p); 18 } 19 void chg(int p,int pos){ 20 if(cl[p]==cr[p]){long long x=read(),y=read();x2[p]=x*x%mod;y2[p]=y*y%mod;xy[p]=x*y%mod;return;} 21 chg(cr[lc]>=pos?lc:rc,pos);up(p); 22 } 23 void ask(int p,int l,int r){ 24 if(l<=cl[p]&&cr[p]<=r){XY=Mod(XY+xy[p]);X2=Mod(X2+x2[p]);Y2=Mod(Y2+y2[p]);return;} 25 if(l<=cr[lc])ask(lc,l,r);if(r>=cl[rc])ask(rc,l,r); 26 } 27 main(){ 28 int n=read(),m=read(),opt,p; 29 build(1,1,n); 30 while(m--){opt=read();p=read(); 31 if(opt==1)chg(1,p); 32 else XY=X2=Y2=0,ask(1,p,read()),printf("%d\n",Mod((1ll*X2*Y2-1ll*XY*XY)%mod+mod)); 33 } 34 }
T3:凤凰院凶真
没想到。《算法竞赛进阶指南》上的原题竟然也不会做了。
想的是三维,表示a串到了i位,b串到了j位,匹配的最大值是k的最大值。
然而最后一维可以压掉,可以发现最后一维与前两维有关。
我们只要强制b串的第j位必选就行了,这样就能得知最后一个选的是几,即最大值是几。
简单的n3枚举。
1 #include<cstdio> 2 int n,m,x[5005],y[5005],fr[5001][5001],sta[5005],top; 3 short dp[5001][5001]; 4 void upd(int i,int j,int li,int lj,int w){ 5 if(dp[i][j]>dp[li][lj]+1)return; 6 dp[i][j]=dp[li][lj]+w; 7 fr[i][j]=lj; 8 } 9 void get_back(int i,int j){if(i==0||j==0)return; 10 int lj=fr[i][j]; 11 if(j!=lj)sta[++top]=y[j]; 12 get_back(i-1,lj); 13 } 14 int main(){ 15 scanf("%d",&n); 16 for(int i=1;i<=n;++i)scanf("%d",&x[i]); 17 scanf("%d",&m); 18 for(int i=1;i<=m;++i)scanf("%d",&y[i]); 19 for(int i=1;i<=n;++i)for(int j=1;j<=m;++j) 20 if(x[i]==y[j]){for(int k=0;k<j;++k)if(y[k]<x[i])upd(i,j,i-1,k,1);} 21 else upd(i,j,i-1,j,0); 22 int M=0;for(int i=1;i<=m;++i)if(dp[n][i]>dp[n][M])M=i; 23 printf("%d\n",dp[n][M]); 24 get_back(n,M); 25 for(int i=top;i;--i)printf("%d ",sta[i]);puts(""); 26 }
优化,边扫边记录上一层的最优决策。n2。
1 #include<cstdio> 2 int n,m,x[5005],y[5005],fr[5001][5001],sta[5005],top; 3 short dp[5001][5001]; 4 void upd(int i,int j,int li,int lj,int w){ 5 if(dp[i][j]>dp[li][lj]+1)return; 6 dp[i][j]=dp[li][lj]+w; 7 fr[i][j]=lj; 8 } 9 void get_back(int i,int j){if(i==0||j==0)return; 10 int lj=fr[i][j]; 11 if(j!=lj)sta[++top]=y[j]; 12 get_back(i-1,lj); 13 } 14 int main(){ 15 scanf("%d",&n); 16 for(int i=1;i<=n;++i)scanf("%d",&x[i]); 17 scanf("%d",&m); 18 for(int i=1;i<=m;++i)scanf("%d",&y[i]); 19 for(int i=1;i<=n;++i){ 20 int bst=0,bp=0; 21 for(int j=1;j<=m;++j){ 22 if(x[i]==y[j])upd(i,j,i-1,bp,1); 23 else upd(i,j,i-1,j,0); 24 if(y[j]<x[i]&&dp[i-1][j]>bst)bst=dp[i-1][j],bp=j; 25 } 26 } 27 int M=0;for(int i=1;i<=m;++i)if(dp[n][i]>dp[n][M])M=i; 28 printf("%d\n",dp[n][M]); 29 get_back(n,M); 30 for(int i=top;i;--i)printf("%d ",sta[i]);puts(""); 31 }
回溯统计答案没什么好说的。
思路积累:
- 当dp维数过多导致复杂度高时,要考虑每维之间是否有联系,能否用一维表示另外一维。