Live2D

Solution -「CF 1366E2」Chiori and Doll Picking (hard version)

Description

  Link.

  给定 {an}, 值域 [0,2m). 对于每个 i[0,m], 求有多少个 {an} 的子序列异或和恰好含有 i 个 bit. 答案模大素数.

  n2×105, m53.

Solution

Defining LATEX macros

  第一个 motivation 应当来自这个诡异的 m53, 通过一定的猜测, 我们发现只有 O(2m/2) 这种复杂度勉强说得过去. 由此, 我们可以尝试在 m 上进行复杂度平衡.

  取 {an} 的一组基底 A. 我们有一个平凡的 O(2rank(A)) 的算法: 枚举 vspan(A), {an} 的线性组合出 v 的方案数恒为 2n|A|, 将其贡献向 |v| 处的答案即可. 接下来只需要找到一个 O(2mrank(A)) 的算法就大功告成了.

  设 A(z)=vspan(A)zv, A^ 为其 FWT 的输出向量. 由于

zv×A(z)=A(z) (vspan(A)),

其中叉乘为异或卷积, 下同. 所以

A(z)×A(z)=2rank(A)A(z),

那么

A^A^=2rank(A)A^,

进而得到

A^{0,2rank(A)}m.

  回过头来, 由于

A^(k)=vspan(A)(1)|vk|,

所以当且仅当不存在 vspan(A) 使得 2|vk| 时, A^(k)=2rank(A), 否则 A^(k)=0. 而又显然, 这个命题中的 vspan(A) 可以等价替换为 vA. 这时有一个 key observation: {kA^(k)=2rank(A)} 是一个线性空间 (证明是容易的).

  尝试构造这个空间的一个基底 B. 将 A 扩充到 m×m 并消元为上三角矩阵且主对角线 1 的上方被消为 0, 则此时 B=AT+I (即转置后主对角线取反). 没想到 motivation, 但证明同样容易. 顺带, 我们发现 rank(B)=mrank(A) (但 A,B 并不一定正交, 不知道为什么很多题解这样认为), 所以差不多可以利用 B 来求答案了.

  同样设出 B(z), 根据 B 的来源:

2rank(A)[zk]B(z)=A^(k).

P(z)=vspan(A)z|v|, Q(z) 同理. 欲求

[zk]P(z)=[z0](A(z)×Ck(z))=ifwt(A^C^k),

其中 [zt]Ck(z)=[|t|=k]. 对正逆变换提取 z0 都不复杂, 我们利用 B(z) 手撕这个式子:

[zk]P(z)=2rank(A)mv[zv]B(z)t(1)t(|v|t)(m|v|kt)=2rank(A)ms[zs]Q(z)t(1)t(st)(mskt).

Q(z) 可以用最初的算法 O(2rank(B)) 算出来, 平衡复杂度则得到 O(2m/2) 的算法.

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;

inline char fgc() {
    static char buf[1 << 17], *p = buf, *q = buf;
    return p == q && (q = buf + fread(p = buf, 1, 1 << 17, stdin), p == q) ?
      EOF : *p++;
}

template <typename Tp = int>
inline Tp rint() {
    Tp x = 0, s = fgc(), f = 1;
    for (; s < '0' || '9' < s; s = fgc()) f = s == '-' ? -f : f;
    for (; '0' <= s && s <= '9'; s = fgc()) x = x * 10 + (s ^ '0');
    return x * f;
}

template <typename Tp>
inline void wint(Tp x) {
    if (x < 0) putchar('-'), x = -x;
    if (9 < x) wint(x / 10);
    putchar(x % 10 ^ '0');
}

const int MAXM = 53, MOD = 998244353, INV2 = MOD + 1 >> 1;
int n, m, bino[MAXM + 5][MAXM + 5];

inline int mul(const int u, const int v) { return 1ll * u * v % MOD; }
inline void subeq(int& u, const int v) { (u -= v) < 0 && (u += MOD); }
inline int sub(int u, const int v) { return (u -= v) < 0 ? u + MOD : u; }
inline void addeq(int& u, const int v) { (u += v) >= MOD && (u -= MOD); }
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;
}

struct LinearBase {
    int siz;
    LL bas[MAXM];

    inline void insert(LL x) {
        rep (i, 0, m - 1) if (x >> i & 1) {
            if (!bas[i]) return bas[i] = x, ++siz, void();
            x ^= bas[i];
        }
    }

    inline std::vector<int> spanCount() const {
        std::vector<int> ret(m + 1), seq; seq.reserve(siz);
        rep (i, 0, m - 1) if (bas[i]) seq.push_back(i);
        std::function<void(int, LL)> enumer = [&](const int id, const LL v) {
            if (id == siz) return void(++ret[__builtin_popcountll(v)]);
            enumer(id + 1, v), enumer(id + 1, v ^ bas[seq[id]]);
        };
        enumer(0, 0);
        return ret;
    }

    inline void standardize() {
        rep (i, 0, m - 1) if (bas[i]) {
            rep (j, 0, i - 1) if (bas[j] >> i & 1) {
                bas[j] ^= bas[i];
            }
        }
    }
} A, B;

inline void solveA() {
    int pwr = mpow(2, n - A.siz);
    auto&& P = A.spanCount();
    rep (i, 0, m) wint(mul(P[i], pwr)), putchar("\n "[i < m]);
}

inline void solveB() {
    bino[0][0] = 1;
    rep (i, 1, m) {
        bino[i][0] = 1;
        rep (j, 1, i) bino[i][j] = add(bino[i - 1][j - 1], bino[i - 1][j]);
    }

    A.standardize();
    rep (i, 0, m - 1) if (!A.bas[i]) {
        LL v = 1ll << i;
        rep (j, 0, i - 1) v |= (A.bas[j] >> i & 1) << j;
        B.insert(v);
    }

    int pwr = n < m ? mpow(INV2, m - n) : mpow(2, n - m);
    auto&& Q = B.spanCount();
    rep (i, 0, m) {
        int ans = 0;
        rep (j, 0, m) if (Q[j]) {
            int coe = 0;
            rep (k, 0, std::min(i, j)) {
                (k & 1 ? subeq : addeq)(coe,
                  mul(bino[j][k], bino[m - j][i - k]));
            }
            addeq(ans, mul(coe, Q[j]));
        }
        wint(mul(pwr, ans)), putchar("\n "[i < m]);
    }
}

int main() {
    n = rint(), m = rint();
    rep (i, 1, n) A.insert(rint<LL>());
    if (A.siz <= m + 1 >> 1) solveA();
    else solveB();
    return 0;
}

posted @   Rainybunny  阅读(69)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
历史上的今天:
2022-02-22 Solution -「LOJ #6538」烷基计数 加强版 加强版
点击右上角即可分享
微信分享提示