隐藏页面特效

The 2024 ICPC Asia East Continent Online Contest (II) K.Match

题面

K.Match

给定长度为 n 的两个序列 ab,当且仅当 aibjk 时,aibj 连一条双向边,其中 表示 XOR 运算。对于 [1,n] 范围内的每个 x,计算大小为 x 的匹配数的个数,结果对 998244353 取模。


题解

考虑两个序列 A,B 如何计算,不妨考虑从 k 的最高位开始枚举,对于 k 的下一个位,当前的合法 A,B 一定,我们为了方便讨论,按当前位为 0/1 分为 A0,A1,B0,B1

序列 A0,A1,B0,B1 的大小是单调不增的,为了方便讨论,我们规定 |S| 表示集合 S 的大小, 表示两者有边。

  • k 的下一位为 1,序列 A,B 合法的条件是 A0B1A1B0

    • A0B1 的方案数为 fiA1B0 的方案数为 gi,容易发现,答案为 fg 的一个卷积,即 ansi+j=fi×gj
  • k 的下一位为 0,序列 A,B 的合法条件为 A0B0A1B1,同时对于 A0B1A1B0 一定成立,我们需要考虑如何将两者答案合并。

    • A0B0 的方案数为 fiA1B1 的方案数为 giA0B1 的方案数为 hiA1B0 的方案数为 ti

      • 考虑 A0B1A1B0 的任选性,任选 i 个对其随机排列,容易看出 hi=i!(|A0|i)(|B1|i)ti=i!(|A1|i)(|B0|i)

      • A0B0A1B1 的组合意义同 k1 时的操作,对 fg 做卷积得到 Si+j=fi×gj

    • 为了将 h,tS 合并,枚举三个集合分别选择了 i,j,l 个匹配。

      • 对每组 (i,j),去除 A0B0 使用的 i 对,A1B1 使用的 j 对,每次重新计算一组 S,其中 hu=u!(|A0|iu)(|B1|ju)tu=u!(|A1|ju)(|B0|iu)

      • 先卷 ht 得到 S,再把三者卷起来有 ansi+j+l=hi×tj×Sl

  • 考虑边界情况,定义 solve(A,B,lev) 为递归计算的 A,B 序列,lev 为当前考虑的位。

    • |A|=0|B|=0 时,没有可选项数,ans0=1ansi=0(i>0)

    • lev=1 时,说明前面的位被考虑完毕了,A,B 可以任意组合,计算 ansi=i!(|A|i)(|B|i)

    • 否则将 A,B 按当前位分解为 A0,A1,B0,B1 对下一个 lev 讨论。

按照题意分析复杂度,对于 k 的位为 0 时,计算一次 S 的复杂度是卷积 O(n2) 的,卷一次 ansi+j+l 需要枚举 i,j,并在当前 i,j 下卷出一个 S,时间复杂度是 O(n4) 的,可以优化做到 O(n3logn),但是原算法时间复杂度很小,因为 |A|,|B| 的大小是单调不增的,所以跑不满。

总体时间复杂度 O(n4logk),代码如下。

复制#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef vector<ll> Poly; const int N = 405, mod = 998244353, maxn = 400; ll n, k; ll frac[N], inv[N]; ll ksm(ll a, ll k) { ll res = 1; while (k) { if (k & 1) res = res * a % mod; k >>= 1; a = a * a % mod; } return res; } void init() { frac[0] = inv[0] = 1; for (int i = 1; i <= maxn; i ++ ) frac[i] = frac[i - 1] * i % mod; inv[maxn] = ksm(frac[maxn], mod - 2); for (int i = maxn - 1; i; i -- ) inv[i] = inv[i + 1] * (i + 1) % mod; } ll C(ll x, ll y) { if (x < y || y < 0) return 0; return frac[x] * inv[y] % mod * inv[x - y] % mod; } Poly Covolution(Poly x, Poly y) { Poly tmp(x.size() + y.size() - 1); for (int i = 0; i < x.size(); i ++ ) for (int j = 0; j < y.size(); j ++ ) (tmp[i + j] += x[i] * y[j]) %= mod; return tmp; } Poly calc(int x, int y) { Poly tmp(min(x, y) + 1); for (int i = 0; i <= min(x, y); i ++ ) tmp[i] = C(x, i) * C(y, i) % mod * frac[i] % mod; return tmp; } Poly solve(Poly x, Poly y, int lev) { if (x.empty() || y.empty()) return Poly(1, 1); if (lev == -1) return calc(x.size(), y.size()); Poly X[2], Y[2]; for (auto i : x) X[i >> lev & 1].push_back(i); for (auto i : y) Y[i >> lev & 1].push_back(i); if (k >> lev & 1) return Covolution(solve(X[1], Y[0], lev - 1), solve(X[0], Y[1], lev - 1)); Poly tmp[2], res(min(x.size(), y.size()) + 1); tmp[0] = solve(X[0], Y[0], lev - 1), tmp[1] = solve(X[1], Y[1], lev - 1); for (int i = 0; i < tmp[0].size(); i ++ ) for (int j = 0; j < tmp[1].size(); j ++ ) { Poly c = Covolution(calc(X[0].size() - i, Y[1].size() - j), calc(X[1].size() - j, Y[0].size() - i)); for (int l = 0; l < c.size(); l ++ ) (res[i + j + l] += tmp[0][i] * tmp[1][j] % mod * c[l]) %= mod; } return res; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr); init(); cin >> n >> k; Poly a(n), b(n); for (int i = 0; i < n; i ++ ) cin >> a[i]; for (int i = 0; i < n; i ++ ) cin >> b[i]; Poly ans = solve(a, b, 60); ans.resize(n + 1); for (int i = 1; i <= n; i ++ ) cout << ans[i] << "\n"; return 0; }

__EOF__

本文作者Yip.Chip
本文链接https://www.cnblogs.com/YipChipqwq/p/18554360.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   YipChip  阅读(52)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
点击右上角即可分享
微信分享提示