P8944 题解
非矩乘做法。理论上常数应该小点,但没跑进最优解第一页。可以当个好写做法看。
首先发现变换后的答案分布仅与变换前的答案分布有关。所以来研究一次变换中单点的变化。
设 \(p_u\) 表示 \(x\) 在 \(u\) 处的概率,考虑选到几个 \(u\),容易写出
\(p'_u\) 是操作前对应答案。
第一项代表两个下标均不等于 \(u\),第二项代表其中之一为 \(u\),第三项代表两项均为 \(u\)。
随便化简一下得到 \(p_u \gets \dfrac{2}{n^2} + \dfrac{n-2}{n} p'_u\)。看到这种线性递推,应该立刻想到不动点。
解 \(X=\dfrac{2}{n^2} + \dfrac{n-2}{n}X\) 得到 \(X=\dfrac{1}{n}\)。两边减不动点得到 \(p_u - \dfrac{1}{n} = \dfrac{n-2}{n}(p'_u - \dfrac{1}{n})\)。然后直接算就行了。\(n\) 的逆元一定存在,算起来很方便。
把 \(\left(\dfrac{n-2}{n}\right)^k\) 算出来就能 \(O(1)\) 求 \(ans_i\)。总复杂度就是线性。
本来想说也不是 dp 做法的。想了想 dp 与递推关系挺紧密的,而我觉得这偏递推,也就不这么说了。
为什么用 \(p'_u\) 是因为显然操作后的一项只与操作一次前有关。
为什么出题人要用这么一个爆 int
的膜数和刚好卡着 long long
的 \(k\) 啊?全开 unsigned long long
才过。中间一直只有 \(1\) 分一度以为自己错得离谱。
#include <cstdio>
#include <assert.h>
#define LL unsigned long long
using namespace std;
typedef unsigned long long ull;
typedef unsigned int uint;
typedef long long ll;
const uint mod = 3221225473u;
const int N = 20000010;
LL qpow(LL a, ull b){
long long ans = 1ll;
for(; b; b >>= 1) {if(b & 1) ans = 1ll * ans * a % mod; a = 1ll * a * a % mod;}
return ans;
}
LL inv(LL n) {return qpow(n, mod-2);}
ull seed;
ull getnext() {
seed ^= seed << 13;
seed ^= seed >> 17;
seed ^= seed << 5;
return seed;
}
uint rd(uint l, uint r) {
return getnext() % (r - l + 1) + l;
}
int n; ull k; uint b[N];
int main() {
scanf("%d%llu%llu", &n, &k, &seed);
ull sum = 0;
for (int i = 1; i < n; ++ i) b[i] = rd(2u, mod - 1), (sum += b[i]) %= mod;
b[n] = mod + 1 - sum;
LL ans = 0;
LL invn = inv(n), s = qpow(invn * (n-2) % mod, k);
for(int i = 1; i <= n; i++) {
LL t = (long long)b[i] + mod - invn; t %= mod;
t = t * s % mod;
t += invn; t %= mod;
ans ^= (t * i);
}
printf("%lld\n", ans);
}
本文来自博客园,作者:purplevine,转载请注明原文链接:https://www.cnblogs.com/purplevine/p/17054188.html