[考试反思]1012csp-s模拟测试70:盘旋
这套题比较烂。。。
上来看到T2是原题,一想上一次考试遇到原题就不换,这次应该也是,于是直接开始码,码了一半然后换题了
T1打表找规律或者推式子都不难。。。
T2水的一匹暴力剪枝即可,但是我并不知道数据那么那么水所以还花了很多时间优化
T3神奇的大模拟,挺有意思但是考场上不可能有人能拿到20+
所以因为题比较烂,所以我就上去了?
隔了一场之后RP守恒又恢复了?
啊啊不要乱说啊再过十几分钟就又要考一场了啊。。。
T1:木板
具体化式子也就是个初中数学,结论就是(最大平方因子的平方根-1)<<3,打个100的表就出来了
1 #include<cstdio> 2 int main(){ 3 long long n,mx;scanf("%lld",&n); 4 while(n){ 5 for(long long i=1;i*i<=n;++i)if(n%(i*i)==0)mx=i; 6 printf("%lld\n",(mx-1)<<3);scanf("%lld",&n); 7 } 8 }
T2:打扫卫生
看到数据范围,直接上根号。
dp[i]表示以i为某一段的结尾,到i为止的最优dp值。
几个比较明显的性质:
0)$dp[i]<=i$
1)颜色数超过$\sqrt{i}$时不必再考虑从前面转移。否则转移来的值不满足上一条
2)任意$i<j$有$dp[i]<=dp[j]$
运用第二条打一个暴力,及时跳出。
1 #include<cstdio> 2 #include<iostream> 3 using namespace std; 4 int dp[40005],n,m,a[40005],al[40005],cal; 5 int min(int a,int b){return a<b?a:b;} 6 int main(){//freopen("2.in","r",stdin);freopen("bl.out","w",stdout); 7 scanf("%d%d",&n,&m); 8 for(int i=1;i<=n;++i){scanf("%d",&a[i]);if(a[i]==a[i-1])i--,n--;} 9 for(int i=1;i<=n;++i)dp[i]=i; 10 for(int i=2;i<=n;++i){ 11 int knd=0; 12 for(int j=i-1;~j;--j){cal++; 13 knd+=al[a[j+1]]==i?0:1; 14 if(knd*knd>=dp[i])break; 15 al[a[j+1]]=i; 16 dp[i]=min(dp[i],dp[j]+knd*knd); 17 } 18 }printf("%d\n",dp[n]);std:cerr<<cal<<endl; 19 }
然后你就AC了。
显然复杂度还是$O(n^2)$的。对拍随机数据,其最内层循环进入了900000000次。
可见题目的数据还不如随机数据。
优化,上述做法的复杂度瓶颈在于你只保证了你扫了$\sqrt{i}$个颜色,但不一定是$\sqrt{i}$个位置。
那么其实可以发现,从i往前扫,到连续的一段区间上的颜色数都相等,根据那第2条性质,那么你直接从这个区间的左端点转移即可。
那么现在的问题是:你要O(1)找到从位置i往前最远到哪里有恰好k种颜色。
考虑位置i,它的颜色为p,那么你找到上一次p出现的位置,这个位置以后再也不会贡献颜色数了,以后不会从它转移。
那么很自然的想法就是一个链表,把上一个p跳过,这一个p加入。
这样沿着链表往前跳,每次必然遇到一个新颜色,颜色数+1。
根据性质1,你最多跳$\sqrt{i}$种颜色。
总复杂度$n\sqrt{n}$。
1 #include<cstdio> 2 int dp[40005],n,m,a[40005],lp[40005],lst[40005],nxt[40005]; 3 int min(int a,int b){return a<b?a:b;} 4 int main(){ 5 scanf("%d%d",&n,&m); 6 for(int i=1;i<=n;++i){scanf("%d",&a[i]);if(a[i]==a[i-1])i--,n--;} 7 for(int i=1;i<=n;++i)dp[i]=i; 8 lst[40001]=0;nxt[0]=40001; 9 for(int i=1;i<=n;++i){ 10 int knd=0,l=lp[a[i]]; 11 lp[a[i]]=i; 12 if(l)lst[nxt[l]]=lst[l],nxt[lst[l]]=nxt[l]; 13 nxt[lst[40001]]=i,lst[i]=lst[40001],lst[40001]=i,nxt[i]=40001; 14 for(int j=lst[40001];j;j=lst[j]){ 15 knd++; 16 if(knd*knd>=dp[i])break; 17 dp[i]=min(dp[i],dp[lst[j]]+knd*knd); 18 } 19 }printf("%d\n",dp[n]); 20 }
T3:骆驼
大模拟。
结论是5*5的网格任意选取起点终点都有解。
所以把这些5*5网格拼接一下就好了。
分奇偶讨论,有些人需要特判5和15的点。
最好好好利用关键位置(3,3),能简单不少(但是我用的不是。。所以特别麻烦)
大网格的拼接也可以搜索,不一定要执迷于for循环自行讨论。。。
终于打出来了!
1 #include<cstdio> 2 int n,s[6][6][6][6][6][6],ans[1001][1001],sx,sy,ex,ey,ord[201][201]; 3 const int xx[]={0,0,3,-3,2,2,-2,-2}; 4 const int yy[]={3,-3,0,0,2,-2,2,-2}; 5 #define tx x+xx[i] 6 #define ty y+yy[i] 7 bool sch(int stp,int x,int y){ 8 if(stp==25)return x==ex&&y==ey; 9 if(x==ex&&y==ey)return false; 10 for(int i=0;i<=7;++i)if(tx>0&&ty>0&&tx<=5&&ty<=5&&!s[sx][sy][ex][ey][tx][ty]){ 11 s[sx][sy][ex][ey][tx][ty]=stp+1; 12 if(sch(stp+1,tx,ty))return true; 13 s[sx][sy][ex][ey][tx][ty]=0; 14 }return false; 15 } 16 void fill(int x,int y,int sx,int sy,int ex,int ey){ 17 for(int i=1;i<=5;++i)for(int j=1;j<=5;++j)ans[x*5-5+i][y*5-5+j]=s[sx][sy][ex][ey][i][j]+6+25*ord[x][y]; 18 } 19 int main(){scanf("%d",&n); 20 for(int i=1;i<=5;++i)for(int j=1;j<=5;++j)for(int k=1;k<=5;++k)for(int l=1;l<=5;++l) 21 s[sx=i][sy=j][ex=k][ey=l][i][j]=1,sch(1,i,j); 22 int bl=n/5; 23 if(bl==1)for(int i=1;i<=5;++i)for(int j=1;j<=5;++j)ans[i][j]=s[1][1][3][3][i][j]; 24 else if(bl==3){ 25 for(int i=1;i<=5;++i)for(int j=1;j<=5;++j)ans[i][j]=s[1][1][3][3][i][j]>6?n*n-25+s[1][1][3][3][i][j]:s[1][1][3][3][i][j];//(1,1) 26 ord[3][1]=1;ord[3][2]=2;ord[3][3]=3; 27 ord[2][3]=4;ord[1][3]=5;ord[2][2]=6;ord[1][2]=7; 28 fill(2,1,2,1,5,3);fill(3,1,2,1,5,3);fill(3,2,5,1,3,5);fill(3,3,3,3,1,3);fill(2,3,3,3,1,3); 29 fill(1,3,3,3,5,1);fill(2,2,2,4,1,3);fill(1,2,3,3,1,2); 30 } 31 else if(bl&1){int alc=-2; 32 for(int i=1;i<=bl;++i)ord[i][1]=++alc; 33 for(int i=bl;i>2;--i)if(i&1)for(int j=2;j<=bl;++j)ord[i][j]=++alc; 34 else for(int j=bl;j>1;--j)ord[i][j]=++alc; 35 ord[2][bl]=++alc;ord[1][bl]=++alc; 36 for(int i=bl-1;i>1;--i)if(i&1)ord[1][i]=++alc,ord[2][i]=++alc; 37 else ord[2][i]=++alc,ord[1][i]=++alc; 38 for(int i=2;i<bl;++i)fill(i,1,2,1,5,3); 39 fill(bl,1,2,1,3,4); 40 for(int i=1;i<=5;++i)for(int j=1;j<=5;++j)ans[i][j]=s[1][1][3][3][i][j]>6?n*n-25+s[1][1][3][3][i][j]:s[1][1][3][3][i][j];//(1,1) 41 for(int i=4;i<=bl;i+=2)for(int j=2;j<=bl;++j)fill(i,j,3,5,1,2); 42 for(int i=3;i<=bl;i+=2)for(int j=2;j<=bl;++j)fill(i,j,3,2,1,5); 43 for(int i=2;i<bl-1;i+=2)fill(2,i,3,3,1,3),fill(1,i,3,3,3,1); 44 for(int i=3;i<bl;i+=2)fill(2,i,3,3,3,1),fill(1,i,3,3,5,3); 45 fill(1,bl,5,3,5,1);fill(2,bl,3,5,3,3);fill(2,bl-1,2,4,1,3);fill(1,2,3,3,1,2);fill(1,bl-1,3,3,3,1); 46 }else{int alc=-2; 47 for(int i=1;i<=bl;++i)ord[i][1]=++alc; 48 for(int i=bl;i;--i)if(i&1)for(int j=bl;j>1;--j)ord[i][j]=++alc; 49 else for(int j=2;j<=bl;++j)ord[i][j]=++alc; 50 for(int i=2;i<bl;++i)fill(i,1,2,1,5,3); 51 fill(bl,1,2,1,3,4); 52 for(int i=1;i<=5;++i)for(int j=1;j<=5;++j)ans[i][j]=s[1][1][3][3][i][j]>6?n*n-25+s[1][1][3][3][i][j]:s[1][1][3][3][i][j];//(1,1) 53 for(int i=1;i<=bl;i+=2)for(int j=2;j<=bl;++j)fill(i,j,3,5,1,2); 54 for(int i=2;i<=bl;i+=2)for(int j=2;j<=bl;++j)fill(i,j,3,2,1,5); 55 } 56 for(int i=1;i<=n;++i,puts(""))for(int j=1;j<=n;++j)printf("%3d ",ans[i][j]); 57 }