第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
每次的合并操作相当于每个数往上走一步或是往左上走一步,那么,最终第ii个数在第步走到位置0
(将原来1~n重新编号为0~n-1,这样可以方便计算)的方案数为CijCji(在总共的j步中需要走i步向左上角的)。那么,我们会发现,一个数对答案有贡献只有当CijCji为奇数,即Cijmod2=1Cjimod2=1,考虑卢卡斯定理,Cijmod2=Ci/2j/2∗Cimod2jmod2mod2Cjimod2=Cj/2i/2∗Cjmod2imod2mod2,相当于每次把i和j的二进制最后一位抓出来再删掉,只有当jmod2=0,imod2=1jmod2=0,imod2=1时,Cimod2jmod2mod2=0Cjmod2imod2mod2=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(nlogn)
代码
#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;
}