第3步a0^a1^a1^a2^a1^a2^a2^a3
第2步a0^a1^a1^a2 a1^a2^a2^a3
第1步a0^a1 a1^a2 a2^a3
第0步a0 a1 a2 a3
每次的合并操作相当于每个数往上走一步或是往左上走一步,那么,最终第 i i 个数在第步走到位置0
(将原来1~n重新编号为0~n-1,这样可以方便计算)的方案数为 Cij C j i (在总共的j步中需要走i步向左上角的)。那么,我们会发现,一个数对答案有贡献只有当 Cij C j i 为奇数,即 Cijmod2=1 C j i m o d 2 = 1 ,考虑卢卡斯定理, Cijmod2=Ci/2j/2∗Cimod2jmod2mod2 C j i m o d 2 = C j / 2 i / 2 ∗ C j m o d 2 i m o d 2 m o d 2 ,相当于每次把i和j的二进制最后一位抓出来再删掉,只有当 jmod2=0,imod2=1 j m o d 2 = 0 , i m o d 2 = 1 时, Cimod2jmod2mod2=0 C j m o d 2 i m o d 2 m o d 2 = 0 ,所以当i为j的子集时,i对第j步的答案有贡献。
所以我们要做的就是计算j的所有子集的异或和,可以用高维前缀和。
计算前缀和有两种办法,一种是利用容斥,即
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+a[i][j];
还有另一种方法
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)sum[i][j]=sum[i-1][j]+a[i][j];
for(int j=1;j<=m;j++)
for(int i=1;i<=n;i++)sum[i][j]+=sum[i][j-1];
其中的第二种方法对于高维前缀和也是适用的。
如果要计算子集的前缀和,就可以将二进制数的每一位看成一维,然后计算高维前缀和。
代码如下:
for(int j=0;j<=log2(n);j++)
for(int i=0;i<n;i++) if((i>>j)&1)a[i]^=a[i^(1<<j)];
时间复杂度: O(nlogn) O ( n l o g n )
代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define maxn 8000006
#define LL long long
using namespace std;
int n,b,c,d,a[maxn];
LL ans;
int main(){
freopen("C.in","r",stdin);
freopen("C.out","w",stdout);
scanf("%d%d%d%d%d",&n,&a[0],&b,&c,&d);int N=log2(n);
for(int i=1;i<n;i++)a[i]=((LL)a[i-1]*a[i-1]%d+(LL)a[i-1]*b%d+c)%d;
for(int j=0;j<=N;j++)
for(int i=0;i<n;i++)
if((i>>j)&1)a[i]^=a[i^(1<<j)];
for(int i=0;i<n;i++)ans^=(LL)a[i]*((LL)i+1);
printf("%lld\n",ans);
return 0;
}