Codeforces 980D
这题其实挺水的,但我比较vegetable,交了好多次才过。
题意:
给定一个序列,把这个序列的所有连续子序列分组,每组中任意两个数相乘是个完全平方数,输出每个子序列最少分的组数;
思路:
先把每个数都除去自身的完全平方因子,为什么呢?这样处理了之后,只有相同的数相乘才能变成完全平方数,而且原来相乘能变成完全平方数的数对也不会有影响,举个例子:$ 1 \times 4 = 4 $,$4$是完全平方数,除去平方因子后,变成 $1 \times 1 = 1$,$1$还是完全平方数(感性地理解YY一下就好了)
把处理完的数列从小到大排序,再离散化(防止数字太大,爆MLE)
由于题目给定n的范围是$5000$,所以不能$ O(n^3) $爆扫,QXZ大佬介绍了一种非常gin的方法,我用了离散化+桶的方法;
开个桶bo[i][j]表示1到i中数值为j的个数;然后dp,f[i][j]表示i到j的数列需要分成几个区间,f[i][j]=f[i][j-1],当第j个数在i到j中唯一时,f[i][j]++;
需要注意的点:
1、除去平方因子时要用while或者倒着扫i,防止有多个平方数;
2、排序时,要记录原来的位置,离散化完要排列回原来的序列(要按原来给定序列的子序列,而且子序列在原序列中是连续的)
3、0要特判,当0为一个单独的子序列时自成一组,否则与其他任何数都能变成完全平方数(和任何数都能一组)
附上蒟蒻的代码:
1 #include<cstdio> 2 #include<cmath> 3 #include<algorithm> 4 using namespace std; 5 const int MAXN=5001; 6 struct rec 7 { 8 int place,num; 9 }a[MAXN],now; 10 bool comp1(const rec &x,const rec &y) 11 { 12 return x.num<y.num; 13 } 14 bool comp2(const rec &x,const rec &y) 15 { 16 return x.place<y.place; 17 } 18 int n,bo[MAXN][MAXN],f[MAXN][MAXN],ans[MAXN],special0; 19 bool special[MAXN]; 20 main() 21 { 22 scanf("%d",&n); 23 for(int i=1;i<=n;++i) 24 { 25 scanf("%d",&a[i].num); 26 a[i].place=i; 27 } 28 for(int i=1;i<=n;++i) 29 { 30 if(a[i].num!=0) 31 { 32 int k=sqrt(abs(a[i].num)); 33 for(int j=k;j>=2;--j) 34 while(a[i].num%(j*j)==0) 35 { 36 a[i].num/=j*j; 37 } 38 } else 39 { 40 special[i]=true; 41 special0=i; 42 } 43 } 44 sort(a+1,a+n+1,comp1); 45 now.place=1; 46 now.num=a[now.place].num; 47 a[now.place].num=1; 48 for(int i=2;i<=n;++i) 49 { 50 if(a[i].num==now.num) 51 { 52 a[i].num=a[now.place].num; 53 } else 54 { 55 now.place=i; 56 now.num=a[i].num; 57 a[i].num=a[i-1].num+1; 58 } 59 } 60 sort(a+1,a+n+1,comp2); 61 for(int i=1;i<=n;++i) 62 for(int j=i;j<=n;++j) 63 bo[j][a[i].num]++; 64 special0=a[special0].num; 65 for(int i=1;i<=n;++i) 66 { 67 f[i][i]=1; 68 ++ans[f[i][i]]; 69 } 70 for(int i=1;i<=n;++i) 71 for(int j=i+1;j<=n;++j) 72 { 73 f[i][j]=f[i][j-1]; 74 if(bo[j][a[j].num]-bo[i-1][a[j].num]==1&&(!special[j])&&(((bo[j][special0]-bo[i-1][special0]!=j-i)||special0==0))) 75 ++f[i][j]; 76 ++ans[f[i][j]]; 77 } 78 for(int i=1;i<=n;++i) 79 printf("%d ",ans[i]); 80 return 0; 81 }