题目
题目概要
求出这个集合:
S
=
{
(
x
+
y
)
m
o
d
M
∣
x
∈
A
,
y
∈
B
}
S=\big\{(x+y)\bmod M\;\big|\;x\in A,\;y\in B\big\}
S={(x+y)modM∣∣x∈A,y∈B}
其中 A A A 和 B B B 是两个整数集合,并满足 A ∪ B = [ 0 , M ) ∩ Z A\cup B=[0,M)\cap\Z A∪B=[0,M)∩Z 。
可以证明 [ 0 , M ) ∩ Z [0,M)\cap\Z [0,M)∩Z 中 S S S 的补集的大小不超过 ∣ A ∣ |A| ∣A∣,所以只需输出其补集。
数据范围与约定
1
⩽
∣
A
∣
⩽
2
×
1
0
5
1\leqslant|A|\leqslant 2\times 10^5
1⩽∣A∣⩽2×105 但
M
⩽
1
0
9
M\leqslant 10^9
M⩽109 。
思路
假设
k
∉
S
k\notin S
k∈/S 。考虑一个
a
(
a
∈
A
)
a\;(a\in A)
a(a∈A) 。若
(
k
−
a
)
m
o
d
M
(k-a)\bmod M
(k−a)modM 在
B
B
B 中,那么二者相加即可得到
k
k
k 。故
∀
a
∈
A
,
(
k
−
a
)
m
o
d
M
∈
A
\forall a\in A,\;(k-a)\bmod M\in A
∀a∈A,(k−a)modM∈A
所以, A A A 中的元素可以 “配对”,并且每一个元素都出现在了某个 “配对” 中。注意一个元素可以跟自己 “配对”。
从数轴上直观感受,则 A A A 是关于 k k k 对称 的。联想到回文。其实就是 差分数组 构成回文串。当然,要求是 A A A 从小到大排序。
然而问题是在模 M M M 剩余系下的;一个元素 x x x 实际上代表了 x + κ M ( k ∈ Z ) x+\kappa M\;(k\in\Z) x+κM(k∈Z) 。在数轴上,把这些数字都标记为 A A A 中元素,就行了。毕竟一个元素本来就被允许用于 “配对” 多次。
不难发现,连续的 n n n 个数总是恰好表示原本的 A A A 。所以在这个 expand- A \text{expand-}A expand-A 上,找到差分值的回文串即可。同时我们发现,这个无限大 expand- A \text{expand-}A expand-A 的差分值是循环的,所以只需要保留 x x x 和 ( x + M ) (x{+}M) (x+M) 。用 m a n a c h e r \tt manacher manacher 即可 O ( n ) \mathcal O(n) O(n) 求出所有答案。
但是输出答案恐怕需要 O ( n log n ) \mathcal O(n\log n) O(nlogn) 排序……
代码
#include <cstdio>
#include <iostream>
#include <vector>
#include <set>
using namespace std;
inline int readint(){
int a = 0, f = 1; char c = getchar();
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
inline void writeint(long long x){
if(x < 0) putchar('-'), x = -x;
if(x > 9) writeint(x/10);
putchar((x%10)^48);
}
# define MB template < typename T >
MB void getMax(T &a,const T &b){ if(a < b) a = b; }
MB void getMin(T &a,const T &b){ if(b < a) a = b; }
const int MaxN = 200020<<2, infty = (1<<30)-1;
int n, r[MaxN], a[MaxN], d[MaxN], M; set<int> ans;
# define add(x,y) ((0ll+(x)+(y))%M)
void manacher(){
for(int i=0; i<=(n<<1); ++i){
d[i<<1] = a[i+1]-a[i];
d[i<<1|1] = -5201314; // 特♀殊字符
}
d[0] = -infty, d[n<<2] = infty;
// for(int i=0; i<=(n<<2); ++i)
// printf("%d ",d[i]); puts("");
for(int i=1,id=0; i<(n<<2); ++i){
if(i < id+r[id])
r[i] = min(id+r[id]-i,r[id*2-i]);
while(d[i-r[i]-1] == d[i+r[i]+1])
++ r[i];
if(i+r[i] > id+r[id]) id = i;
if(r[i] >= n-1){
int x = a[(i+1)>>1], y = a[(i+2)>>1];
ans.insert(add(x,y));
}
// i为偶:a[i>>1]+a[(i>>1)+1]
// i为奇:a[(i+1)>>1]<<1
}
}
int main(){
n = readint(), M = readint();
for(int i=1; i<=n; ++i)
a[i] = readint(), a[i+n] = a[i]+M;
manacher(), printf("%d\n",int(ans.size()));
for(auto i : ans) printf("%d ",i); puts("");
return 0;
}