Description
对于一个数组 A 和数字 X,让我们定义 f(A,X) 如下:
如果不能将 A 拆分为几个子段使得每个子数组中所有元素的异或不等于 X,则 f(A,X)=0。
否则,f(A,X) 等于这种拆分中最大可能的子段数。
给定整数 N,K 和 X,其中 0≤X<2K。 考虑长度为 N 的数组 A,如果每个元素都是从 0 到 2K−1 均匀生成的整数。 求 f(A,X) 的期望值对 998244353 取模的值。
1≤N≤106,1≤K≤60,0≤X<2K。
Solution
考虑将 A 的前缀异或和数组 S 拿出来,题目转化为选出最多的断点,满足 i0=0<i1<i2<…<ik=n,且 Sij⊕Sij−1=X。
首先如果 X=0,那么显然 f(A,X) 等于 S 中极长连续段的个数减一。
如果 X=0,考虑用类似 X=0 的做法,将 v 和 v⊕X 看成一种数去缩连续段,那么不同连续段之间显然不互相影响。而对于连续段内,如果当前是开始或者末尾连续段,个数即为开始或者末尾对应的数的个数,否则个数为这个连续段内两种数的出现次数最大值。容易发现这么做是最优的。
然后考虑怎么计数。
设 fi 表示长度为 i 的连续段(不在开始或者末尾)的贡献。可以得到:
fi=j=0∑i(ji)max{j,i−j}
这个东西可以分类讨论 max{j,i−j} 的类型然后递推求。
对于前缀和后缀串的贡献容易得到。
时间复杂度:O(n)。
Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
| #include <bits/stdc++.h>
#define int int64_t
const int kMaxN = 1e6 + 5, kMod = 998244353;
int n, k, x, cnt; int fac[kMaxN], ifac[kMaxN], f[kMaxN], g[kMaxN];
int qpow(int bs, int64_t idx = kMod - 2) { int ret = 1; for (; idx; idx >>= 1, bs = (int64_t)bs * bs % kMod) if (idx & 1) ret = (int64_t)ret * bs % kMod; return ret; }
inline int fix(int x) { return (x % kMod + kMod) % kMod; } inline int add(int x, int y) { return (x + y >= kMod ? x + y - kMod : x + y); } inline int sub(int x, int y) { return (x >= y ? x - y : x - y + kMod); } inline void inc(int &x, int y) { (x += y) >= kMod ? x -= kMod : x; } inline void dec(int &x, int y) { (x -= y) < 0 ? x += kMod : x; } inline int getop(int x) { return (~x & 1) ? 1 : (kMod - 1); }
int C(int m, int n) { if (m < n || m < 0 || n < 0) return 0; return 1ll * fac[m] * ifac[n] % kMod * ifac[m - n] % kMod; }
void prework(int n = 1e6) { fac[0] = 1; for (int i = 1; i <= n; ++i) fac[i] = 1ll * i * fac[i - 1] % kMod; ifac[n] = qpow(fac[n]); for (int i = n; i; --i) ifac[i - 1] = 1ll * i * ifac[i] % kMod; }
int getcnt(int n) { if (n == 0) return 0; else if (n == 1) return 1; else return add(1ll * (n - 1) * qpow(2, n - 2) % kMod, qpow(2, n - 1)); }
void dickdreamer() { std::cin >> n >> k >> x; prework(); int ans = 0, cnt = fix(1ll << k), cntg = fix(1ll << (k - 1)); if (x == 0) return void(std::cout << sub(n, 1ll * n * qpow(cnt) % kMod) << '\n'); for (int i = 2; i <= n; ++i) { g[i] = add(2ll * g[i - 1] % kMod, 1ll * getop(i) * C(i - 2, i / 2 - 1) % kMod); } for (int i = 1; i <= n; ++i) { f[i] = sub(2ll * i % kMod * g[i] % kMod, (i % 2 == 0) * (i / 2) * C(i, i / 2) % kMod); f[i] = sub(1ll * i * qpow(2, i) % kMod, f[i]); if (i <= n - 2) inc(ans, 1ll * (n - i - 1) * f[i] % kMod * cntg % kMod * (cnt - 2) % kMod * (cnt - 2) % kMod * qpow(cnt, n - i - 2) % kMod); } for (int i = 1; i <= n - 1; ++i) inc(ans, 1ll * f[i] * (cntg - 1) % kMod * (cnt - 2) % kMod * qpow(cnt, n - i - 1) % kMod); for (int i = 1; i <= n - 1; ++i) { inc(ans, 1ll * getcnt(i) * cnt % kMod * (cnt - 2) % kMod * qpow(cnt, n - i - 1) % kMod); } inc(ans, 1ll * getcnt(n) % kMod * (cnt - 2) % kMod); for (int i = 1; i <= n - 1; ++i) { inc(ans, 1ll * i * qpow(2, i - 1) % kMod * (cnt - 2) % kMod * qpow(cnt, n - i - 1) % kMod); } for (int i = 1; i <= n; ++i) inc(ans, 1ll * i * C(n - 1, i - 1) % kMod); std::cout << 1ll * ans * qpow(qpow(cnt), n) % kMod << '\n'; }
int32_t main() { #ifdef ORZXKR freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout); #endif std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0); int T = 1; while (T--) dickdreamer(); return 0; }
|