题目大意:
题目是将$[0,m)$的数划成了两个集合,其中一个集合的元素个数不超过$n$。问在第一个集合中选出的数加上第二个集合中选出的数的和中没有出现的数有哪些。
题目分析:
很有意思的一道题。方便起见,接下来的所有表述在模意义下进行。选出的数集合用$a_1 \sim a_n$表示
考虑给出的集合,$k$无法由两个集合中任选一个数的和表示等价于对于给出的集合元素$a_i$,$k-a_i$也在给出的集合之中。
这样来看答案不会超过$n$。也就是你对选出的集合中最小的数考虑,它与另一个选出的数配对。且每个选出的数都能这样配对。
假设你现在让$a_1$与$a_i$配对,那么$a_2 \sim a_{i-1}$必然首尾配对。且$a_{i+1}$一定与$a_n$配对,否则$a_n$找不到配对对象。
这样我们将问题化为了两个环。内部要与最外层答案一致意味着从左到右增加了$k$,那么从右到左也要减少$k$,换句话说它的差分数组是回文的。
所以manacher判回文就行了。
代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 4 const int maxn = 200200; 5 6 int n,m; 7 int a[maxn],x[maxn],ans[maxn],num; 8 9 int ss[maxn*2],f[maxn*2],ct,rr; 10 11 void read(){ 12 scanf("%d%d",&n,&m); 13 for(int i=1;i<=n;i++) scanf("%d",&a[i]); 14 } 15 16 void manacher(){ 17 for(int i=1;i<=n;i++) ss[i*2] = x[i]; 18 for(int i=1;i<=n;i++) ss[i*2-1] = -1; // to replace '$' 19 ss[2*n+1] = -1; f[1] = 1,rr = 1,ct = 1; 20 for(int i=2;i<=2*n+1;i++){ 21 if(rr < i) f[i] = 1; else f[i] = min(rr-i+1,f[2*ct-i]); 22 while(i+f[i]<=2*n+1&&i-f[i]>=1&&ss[i+f[i]]==ss[i-f[i]])f[i]++; 23 if(i+f[i]-1 > rr) ct = i,rr = i+f[i]-1; 24 } 25 } 26 27 int chk(int l,int r){ 28 if(l >= r) return 1; 29 int cent = l+r; 30 int ll = cent-f[cent]+1,rr = cent+f[cent]-1; 31 if(ll <= 2*l && rr >= 2*r) return 1; 32 else return 0; 33 } 34 35 void work(){ 36 for(int i=1;i<=n;i++) x[i] = a[i] - a[i-1]; 37 if(n == 1){printf("1\n%d",(a[1]+a[1])%m);return;} 38 manacher(); 39 for(int i=1;i<n;i++){ 40 if((a[1] + a[i])%m == (a[i+1] + a[n])%m){ 41 if(chk(2,i)&&chk(i+2,n)){ 42 ans[++num] = (a[1]+a[i])%m; 43 } 44 }else continue; 45 } 46 if(chk(2,n)){ans[++num] = (a[1]+a[n])%m;} 47 sort(ans+1,ans+num+1); 48 printf("%d\n",num); 49 for(int i=1;i<=num;i++){ 50 printf("%d ",ans[i]); 51 } 52 } 53 54 int main(){ 55 read(); 56 work(); 57 return 0; 58 }