题目大意
给你一个长度为 n n n的序列 A A A和一个整数 m m m。
对 k = 1 , 2 , … . m k=1,2,\dots.m k=1,2,….m,求经过如下 k k k次操作后 A n A_n An的值
- 对每个 i ( 1 ≤ i ≤ n ) i(1\leq i\leq n) i(1≤i≤n),同时让 A i = A 1 ⊕ A 2 ⊕ ⋯ ⊕ A i A_i=A_1\oplus A_2\oplus\cdots\oplus A_i Ai=A1⊕A2⊕⋯⊕Ai
1 ≤ n ≤ 1 0 6 , 1 ≤ m ≤ 1 0 6 , 1 ≤ A i ≤ 2 30 1\leq n\leq 10^6,1\leq m\leq 10^6,1\leq A_i\leq 2^{30} 1≤n≤106,1≤m≤106,1≤Ai≤230
题解
如果将异或改为加法,则对数组做 k k k次前缀和,最终 a n a_n an的值为
∑ i = 1 n C k − 1 + i i ⋅ a n − i \sum\limits_{i=1}^nC_{k-1+i}^i\cdot a_{n-i} i=1∑nCk−1+ii⋅an−i
因为本题是异或,所以就按 C k − 1 + i i C_{k-1+i}^i Ck−1+ii的奇偶性来判断贡献。若 C k − 1 + i i m o d 2 = 1 C_{k-1+i}^i\bmod 2=1 Ck−1+iimod2=1,则有贡献;否则没有贡献。
那怎么知道 C n m C_n^m Cnm的奇偶性呢?用lucas定理,对于 n n n和 m m m的每一个二进制位,如果 n n n和 m m m都为 0 0 0或 1 1 1,或者 n n n的这一位为 1 1 1、 m m m的这一位为 0 0 0,则 C n m C_n^m Cnm为奇数。只要有一位不满足上述条件,则 C n m C_n^m Cnm为偶数。
我们发现,当在二进制中 m m m为 n n n的子集时, C n m C_n^m Cnm为奇数。也就是说, a n − i a_{n-i} an−i有贡献,要满足 i ⊂ k − 1 + i i\subset k-1+i i⊂k−1+i时有贡献。那么, k − 1 ⊂ ∁ U i k-1\subset \complement_Ui k−1⊂∁Ui,即 i ⊂ ∁ U k − 1 i\subset \complement_Uk-1 i⊂∁Uk−1。
综上所述,当 i ⊂ ∁ U k − 1 i\subset \complement_Uk-1 i⊂∁Uk−1时, a n − i a_{n-i} an−i对 k k k次操作后的 a n a_n an有贡献。
我们可以先倒着输入,即 a n − i = a i a_{n-i}=a_i an−i=ai。再对 a a a数组求子集和。令 v i v_i vi表示对 a a a求 i i i的子集和,则对于每一个 k k k,令 j = ∁ U k − 1 j=\complement_Uk-1 j=∁Uk−1,则答案为 v j v_{j} vj。
时间复杂度为 O ( n log n ) O(n\log n) O(nlogn)。
code
#include<bits/stdc++.h>
using namespace std;
int n,m,a[(1<<20)+5];
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[n-i]);
for(int j=0;j<=19;j++){
for(int i=0;i<(1<<20);i++){
if(i&(1<<j)) a[i]^=a[i^(1<<j)];
}
}
for(int i=0;i<m;i++){
printf("%d ",a[i^((1<<20)-1)]);
}
return 0;
}