Live2D

Solution -「CF 923E」Perpetual Subtraction

Description

  Link.

  有一个整数 x[0,n],初始时以 pi 的概率取值 i。进行 m 轮变换,每次均匀随机取整数 r[0,x],令 xr。求变换完成后 x=i (i=0..n) 的概率。答案模 998244353

Solution

  令向量 p 为此时 x 的取值概率,显然一次变换是对 p 的线性变换 A,有

A=[1121n1n+1121n1n+11n1n+11n+1].

  我们的目标即为求出 Amp。注意到 A 的特征值很明显——λi=1i+1,i[0,n],所以可以考虑将其对角化来加速矩阵幂的计算。手算一下 λi 所对应的特征向量 vi,发现一组特解

vi=[(i0)(i1)(i2)(1)n(in)],

那么 V=[v0v1vn]

Vij=(1)i(ji).

  尝试对 V 求逆,由于

Vij2=k=0n1(1)i(ki)(1)k(jk)=k=0n1(1)i+k(ji)(jiki)=(1)i(ji)k=0n=1(1)k(jiki)=(1)i(ji)(11)ji=[i=j],

所以 V=V1。因此答案为

V[λ0mλ1mλnm]Vp.

其中 V 的变换效果是一个差卷积,NTT 实现即可。复杂度 O(nlogn)

Code

/*+Rainybunny+*/

#include <bits/stdc++.h>

#define rep(i, l, r) for (int i = l, rep##i = r; i <= rep##i; ++i)
#define per(i, r, l) for (int i = r, per##i = l; i >= per##i; --i)

typedef long long LL;

const int MAXL = 1 << 18, MOD = 998244353, G = 3;
int n, p[MAXL + 5], fac[MAXL + 5], ifac[MAXL + 5];
LL m;

inline int sgn(const int u) { return u & 1 ? MOD - 1 : 1; }
inline int mul(const int u, const int v) { return 1ll * u * v % MOD; }
inline int sub(int u, const int v) { return (u -= v) < 0 ? u + MOD : u; }
inline int add(int u, const int v) { return (u += v) < MOD ? u : u - MOD; }
inline int mpow(int u, int v) {
    int ret = 1;
    for (; v; u = mul(u, u), v >>= 1) ret = mul(ret, v & 1 ? u : 1);
    return ret;
}

inline void init() {
    fac[0] = 1;
    rep (i, 1, n) fac[i] = mul(i, fac[i - 1]);
    ifac[n] = mpow(fac[n], MOD - 2);
    per (i, n - 1, 0) ifac[i] = mul(i + 1, ifac[i + 1]);
}

inline void ntt(const int n, int* u, const int tp) {
    static int rev[MAXL + 5]; int lgn = 31 - __builtin_clz(n);
    rep (i, 1, n - 1) rev[i] = rev[i >> 1] >> 1 | (i & 1) << lgn >> 1;
    rep (i, 0, n - 1) if (i < rev[i]) std::swap(u[i], u[rev[i]]);
    for (int stp = 1; stp < n; stp <<= 1) {
        int wi = mpow(G, (MOD - 1) / (stp << 1));
        for (int j = 0; j < n; j += stp <<1 ) {
            for (int wk = 1, k = j; k < j + stp; ++k, wk = mul(wk, wi)) {
                int ev = u[k], ov = mul(wk, u[k + stp]);
                u[k] = add(ev, ov), u[k + stp] = sub(ev, ov);
            }
        }
    }
    if (!~tp) {
        std::reverse(u + 1, u + n);
        int inv = mpow(n, MOD - 2);
        rep (i, 0, n - 1) u[i] = mul(u[i], inv);
    }
}

inline void transV() {
    static int f[MAXL + 5], g[MAXL + 5];
    int len = 1 << 32 - __builtin_clz((n << 1) - 1);
    rep (i, 0, len - 1) f[i] = g[i] = 0;
    rep (i, 0, n - 1) f[i] = mul(fac[i], p[i]), g[i] = ifac[n - 1 - i];
    ntt(len, f, 1), ntt(len, g, 1);
    rep (i, 0, len - 1) f[i] = mul(f[i], g[i]);
    ntt(len, f, -1);
    rep (i, 0, n - 1) p[i] = mul(mul(sgn(i), ifac[i]), f[n - 1 + i]);
}

int main() {
    scanf("%d %lld", &n, &m);
    ++n, m %= MOD - 1, init();
    rep (i, 0, n - 1) scanf("%d", &p[i]);

    transV();
    rep (i, 0, n - 1) p[i] = mul(p[i], mpow(i + 1, MOD - 1 - m));
    transV();
    rep (i, 0, n - 1) printf("%d%c", p[i], i < repi ? ' ' : '\n');
    return 0;
}

posted @   Rainybunny  阅读(64)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示