比赛的时候没仔细想,赛后一想这题其实挺简单的,先求出序列的异或前缀和,然后将异或前缀和建出一颗trie树,然后我们可以二分答案,把问题变成判定性问题,判定是否存在一种方案,使得所有的分组的异或和都大于等于这个二分的答案,然后就可以dp了,用f[i][j]表示到j为止能不能分成i组,f[i][j]=f[i-1][j-k]|f[i-1][j-k+1]...|f[i-1][j-1],这个东西用trie树维护一下就可以了。单组数据复杂度O(nmlogn^2)
代码
1 #include<cstdio> 2 const int N = 1001010; 3 int num[N],Num[N],trie[N][2],tt,sum[N]; 4 int n,m,l,r,i,k,j,a[N],mid; 5 int flag[11][N]; 6 void add(int x,int y) 7 { 8 int i; 9 for (i=0;i<=31;i++) 10 num[i]=x&1,x>>=1; 11 int t=0; 12 for (i=31;i>=0;i--) 13 { 14 if (trie[t][num[i]]==0) trie[t][num[i]]=++tt; 15 t=trie[t][num[i]]; 16 sum[t]+=y; 17 } 18 } 19 int find(int x,int y) 20 { 21 int i; 22 for (i=0;i<=31;i++) 23 { 24 num[i]=x&1,x>>=1; 25 Num[i]=y&1,y>>=1; 26 } 27 int t=0; 28 for (i=31;i>=0;i--) 29 { 30 if (Num[i]==1) 31 { 32 if (!sum[trie[t][1-num[i]]]) return 0; 33 else t=trie[t][1-num[i]]; 34 } 35 else 36 { 37 if (sum[trie[t][1-num[i]]]) return 1; 38 else if (!sum[trie[t][num[i]]]) return 0; 39 else t=trie[t][num[i]]; 40 } 41 } 42 return 1; 43 } 44 int check(int x) 45 { 46 int i,j; 47 for (i=0;i<=m;i++) 48 for (j=0;j<=n;j++) 49 flag[i][j]=0; 50 flag[0][0]=1; 51 for (i=1;i<=m;i++) 52 { 53 for (j=0;j<=tt;j++) sum[j]=0; 54 for (j=0;j<=n;j++) 55 { 56 if (j-k-1>=0) 57 if (flag[i-1][j-k-1]) add(a[j-k-1],-1); 58 flag[i][j]=find(a[j],x); 59 if (flag[i-1][j]) add(a[j],1); 60 } 61 } 62 return flag[m][n]; 63 } 64 int main() 65 { 66 int test; 67 scanf("%d",&test); 68 for (int ii=1;ii<=test;ii++) 69 { 70 for (i=0;i<=tt;i++) 71 trie[i][0]=trie[i][1]=0; 72 tt=0; 73 scanf("%d%d%d",&n,&m,&k); 74 for (i=1;i<=n;i++) 75 { 76 scanf("%d",&a[i]); 77 a[i]^=a[i-1]; 78 } 79 for (i=0;i<=n;i++) 80 add(a[i],0); 81 l=1;r=2000000000; 82 while (l<=r) 83 { 84 mid=(l+r)>>1; 85 if (check(mid)) l=mid+1;else r=mid-1; 86 } 87 printf("Case #%d: ",ii); 88 printf("%d ",r); 89 } 90 }