[考试反思]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 }
183B

 

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 }
800ms

然后你就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 }
View Code

 

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 }
View Code

 

posted @ 2019-10-13 07:29  DeepinC  阅读(206)  评论(0编辑  收藏  举报