World Of Our Own [Lucas+思维题]

W o r l d   O f   O u r   O w n World\ Of\ Our\ Own World Of Our Own

⼩C在研究数组的xor。
她有⼀个长度为 n n n 的数组 A A A
她每次会把 a i − 1 a_{i-1} ai1 a i a_i ai异或起来,然后 A A A的长度会变成 n − 1 n - 1 n1
这么操作了 n − 1 n - 1 n1次之后得到了长度为 1 1 1的数组。
她想知道在操作过程中每次的 a 1 a_1 a1为多少?
为了避免输出过⼤,请输出第 j j j次操作后的 a 1 × ( j + 1 ) a_1 \times (j + 1) a1×(j+1)的异或和( 0 ≤ j ≤ n − 1 0 \leq j \leq n - 1 0jn1)。

n ≤ 8 ∗ 1 0 6 , a , b , c , d ≤ 1 0 9 , a &lt; d n \le 8 * 10^6, a, b, c, d \le 10^9, a &lt; d n8106,a,b,c,d109,a<d


正 解 部 分 \color{red}{正解部分}

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

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

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

那么什么时候 贡献系数 为奇数呢 ? ? ?, 换句话说, 什么时候 C i j C_i^j Cij 为奇数呢 ? ? ?
考虑在模 2 2 2的意义下使用 L u c a s Lucas Lucas 定理 : C i j ≡ C i % 2 j % 2 ∗ C i / 2 j / 2 m o d &ThinSpace;&ThinSpace; 2 C_i^j ≡ C_{i\%2}^{j\%2}*C_{i/2}^{j/2} \mod 2 CijCi%2j%2Ci/2j/2mod2,

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

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

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


实 现 部 分 \color{red}{实现部分}

可以枚举 j j j, 然后在 j j j 的二进制上 “填” 1 1 1 得到一个可以贡献的 i i i, 累计 a j a_j aj的贡献 .

#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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值