8.29 我们自己的世界

题意

给一个大小为\(N\)的数组\(A\),每次对数组进行如下的变换:

  • \(B[i]=A[i]\oplus A[i+1]\)
  • \(A[i]=B[i]\)

这里的\(\oplus\)运算指异或

我们会发现每次数组\(A\)的长度会减一,当数组\(A\)的长度减至一时,结束变换

如果我们把每次变换后的\(A\)数组的第一项保存下来,记为\(A_0[1],A_1[1]...A_{n-1}[1]\)


\[ \oplus \sum_{i=0}^{n-1}A_i[1] \times (i+1) \]
这里的\(\oplus \sum\)指的是异或和运算

\(N \leq 8\times 10^6\)


解法

这题解法巨巧妙

画出一个类似菱形网格图的结构,观察一下每次变换后的第一项是被哪些元素异或几次组成的

把元素编号由\(1,2..n\)改为\(0,1...n-1\)

我们能观察到如下的规律

变换\(/\)元素 \(a_0\) $a_1 $ $a_2 $ \(...\) \(a_{n-1}\)
\(1\) \(1\) \(0\) \(0\) \(0\) \(0\)
\(2\) \(1\) \(2\) \(1\) \(0\) \(0\)
$... $
\(n\) \(C_n^0\) \(C_n^1\) \(C_n^2\) \(...\) \(C_n^{n-1}\)

这不就是个杨辉三角吗?

我们知道,异或次数为奇数的有贡献,异或次数为偶数的无贡献

那么现在我们需要快速求出组合数的奇偶性

根据卢卡斯定理,我们知道
\[ C_n^m \pmod p = C_{n\%p}^{m\%p} \times C_{n/p}^{m/p} \]
现在我们需要判断其奇偶性,就需要模一个\(2\)
\[ C_n^m \pmod 2 = C_{n\%2}^{m\%2} \times C_{n/2}^{m/2} \]
我们可以把\(\%2\)\(/2\)视作二进制位下的操作,即
\[ C_n^m\pmod 2=C_{n\&1}^{m\&1}\times C_{n>>1}^{m>>1} \]
研究一下\(C_{n\&1}^{m\&1}\)我们发现,只有\(n\)为偶数\(m\)为奇数时这个数为\(0\),其余均为\(1\)

也就是\(n\)的某一位为\(0\)\(m\)的某一位为\(1\)时,这个数为\(0\)

所以我们可以发现
\[ C_n^m \pmod 2 = 1 \ \ (m \& n=m)\\ C_n^m \pmod 2 = 0 \ \ (otherwise) \]
所以当\(m\)\(n\)的子集时,元素\(a_m\)会对第\(n\)次变换的第一个元素有贡献


\[ b_n=\oplus \sum_{d\& n=n}a_d \]
最后的答案即为
\[ ans=\oplus \sum_{i=0}^{n-1} (i+1)\times b_i \]
于是现在的问题转化为了求\(b\)数组,也就是对于数组\(a\)每个位置上求一遍子集和

这个可以用\(FWT\),但更方便的是采用\(FMT\),代码比较短

本质上就是做一个\(n\)维的前缀和,一维一维的累加答案

代码

#include <cstdio>

using namespace std;

const int N = 8e6 + 10;

int n;

long long a, b, c, d;
long long A[N];

int main() {
    
    scanf("%d%lld%lld%lld%lld", &n, &a, &b, &c, &d);
    
    A[0] = a;
    for (int i = 1; i < n; ++i)
        A[i] = (A[i - 1] * A[i - 1] % d + b * A[i - 1] % d + c) % d;
    
    for (int i = 0; i < 23; ++i) 
        for (int j = 0; j < n; ++j) 
            if ((j >> i) & 1)   A[j] ^= A[j ^ (1 << i)];
        
    long long ans = 0;
    for (int i = 0; i < n; ++i) ans ^= A[i] * (i + 1);
    
    printf("%lld\n", ans);  
        
    return 0;       
}

猜你喜欢

转载自www.cnblogs.com/VeniVidiVici/p/11446692.html