World Of Our Own [Lucas+思维题]

World Of Our OwnWorld\ Of\ Our\ Own

⼩C在研究数组的xor。
她有⼀个长度为 nn 的数组 AA
她每次会把ai1a_{i-1}aia_i异或起来,然后AA的长度会变成n1n - 1
这么操作了n1n - 1次之后得到了长度为11的数组。
她想知道在操作过程中每次的a1a_1为多少?
为了避免输出过⼤,请输出第jj次操作后的a1×(j+1)a_1 \times (j + 1)的异或和(0jn10 \leq j \leq n - 1)。

n8106,a,b,c,d109,a<dn \le 8 * 10^6, a, b, c, d \le 10^9, a < d


\color{red}{正解部分}

将过程图画出如下
(请自行脑补将所有下标减11)

图中最左边一列的点从上向下分别表示第 0,1,2,30, 1, 2, 3 此操作后的 a1a_1,
其左下角的数字表示其受数组中对应位置的异或和贡献,

发现贡献系数是符合杨辉三角的规律的, 用语言表达出来就是: ijCij第 i 次操作后受位置 j 影响的贡献系数是 C_{i}^j,
由于是异或操作, 当 贡献系数 是奇数的时候才会真正产生贡献 .

那么什么时候 贡献系数 为奇数呢 ??, 换句话说, 什么时候 CijC_i^j 为奇数呢 ??
考虑在模22的意义下使用 LucasLucas 定理 : CijCi%2j%2Ci/2j/2mod  2C_i^j ≡ C_{i\%2}^{j\%2}*C_{i/2}^{j/2} \mod 2,

先考虑第一项 Ci%2j%2C_{i\%2}^{j\%2}, 当且仅当 i%2=0,j%2=1i\%2=0, j\%2=1 的时候 Ci%2j%2C_{i\%2}^{j\%2}00,
此时 ii 的二进制尾部为 00, jj 的二进制尾部为 11 , 由于是乘法, 这个 00 会导致整个式子归零 .

再考虑第二项, 对 Ci/2j/2C_{i/2}^{j/2} 使用 LucasLucas 定理,
在二进制表示下相当于 iijj 同时去掉尾部的一个二进制位之后的一个递归过程, 回到 考虑第一项 的情况 .

于是综上所述可得: i&j=j,Cij=1,jia1.当 i \& j = j 时, C_{i}^{j}=1, j 位置的数对第 i 次操作的 a_1 有贡献 .


\color{red}{实现部分}

可以枚举 jj, 然后在 jj 的二进制上 “填” 11 得到一个可以贡献的 ii, 累计aja_j的贡献 .

#include<bits/stdc++.h>
#define reg register
typedef long long ll;

int read(){
        char c;
        int s = 0, flag = 1;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ flag = -1, c = getchar(); break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

const int maxn = 8500006;

int N;
int a;
int b;
int c;
int d;
int Rd[maxn];
int sum[maxn];

ll Ans;
ll A[maxn];

void Vio(){
        int cnt = 0;
        for(reg int i = N; i > 1; i --){
                Ans ^= A[1]*(++ cnt);
                for(reg int j = 1; j < i; j ++) A[j] = A[j]^A[j+1];
        }
        Ans ^= A[1]*(++ cnt);
        printf("%lld\n", Ans);
}

void Work(){
        for(reg int j = 0; j <= 22; j ++)
                for(reg int i = 0; i < N; i ++)
                        if(!(i & (1<<j))) A[i|(1<<j)] ^= A[i];
        for(reg int i = 0; i < N; i ++) Ans ^= A[i]*(i+1);
        printf("%lld\n", Ans);
}

int main(){
        scanf("%d%d%d%d%d", &N, &a, &b, &c, &d);
        A[0] = a;
        for(reg int i = 1; i < N; i ++) A[i] = (A[i-1]*A[i-1] + 1ll*b*A[i-1] + c) % d;
        Work();
        return 0;
}
posted @ 2019-08-30 14:29  XXX_Zbr  阅读(198)  评论(0编辑  收藏  举报