社论 22.10.7

CF1603F

给定三个正整数 n,k,x,求满足以下条件的序列 a1...n 的个数:

  • 对于每一个 1in0ai<2k
  • 序列 a 没有异或和为 x 的非空子序列。

答案对 998244353 取模。

1n109,k1×107,0x<2min(64,k)

数学题。

转化题意,序列的每个元素等价于 F2k 内的一个向量。因此我们需要选出 F2k 内的 n 个向量,满足他们构成的空间内不存在向量 x,求选择方案。分情况讨论:

x=0

问题转为选出 F2kn 个线性无关的向量。
这是个经典问题,又可以转化成选出一个满秩的 n×k 01 矩阵的方案数。
答案即为

i=0n1(2k2i)

证明类似这个,这里从略。

可以 O(k) 地计算答案。

x>0

容易发现在 F2k 内任意向量等价。因此任意 x>0 的答案相同。不妨令 x=1。我们令选出的 n 个向量与 1 构成一个 (n+1)×k 矩阵。


先不考虑dp。直接按组合意义容斥可得答案。
我们考虑不包含 x 的维度为 d 的向量空间的基底数量,同上可以得到计算式

i=0d1(2k2i)(2d1)i=1d1(2k2i)=i=1d(2k2i)

然后考虑剩下的 nd 个向量,他们应当被基底表出。考虑第 i 个向量能表出的向量可以表为 (1+2ix+22ix2+),于是立得答案为

[xnd]i=0d(1+2ix+22ix2+)=[xnd ]i=0d112ix

如果你熟悉 q-binomial,那这玩意就等于是 q=2 的情况,于是立得

[xnd ]i=0d112ix=[nnd]2=[n]2![nd]2![d]2!=i=1n(2i1)i=1d(2i1)i=1nd(2i1)

答案即为

d=0ni=1d(2k2i)×[nnd]2

如果你不熟悉 q-binomial,那就去熟悉
我们考虑表出 F(x)=[xnd ]i=0n11qix。用两种方式消掉 i=0 的项得到

(1x)F(x)=(1qn+1x)F(qx)

两边取 [xn],经过简单移项可得递推关系式。


然后考虑dp,设 fi,r 为选了 i 个向量,当前矩阵的秩为 r 的方案数。我们考虑新加入的向量是否能被先前除 1 外的向量表出,立即有方程

fi,r=fi1,r×2r1+fi1,r1×(2k2r1)

得到了 O(nk) 的做法。

考虑按第一维求和得到 Fr

Fr(x)=i=0fi,rxi

带入 fi,r 递推式立得 Fr 递推式:

Fr(x)=(2k2r1)x12r1xFr1(x)=i=1r(2k2i1)x12i1x=12k1i=1r(2k2i1)xri=1r112i1x

答案即为

r=1k[xn+1]Fr(x)

考虑记

Gr(x)=i=1r112i1x

容易发现这是 q
我们承接上面消最低次项的思路得到 (1x)F(x)=(12n+1x)F(2x),于是提取系数得 [xn]Fr(x)=i=1n2ni+112i1
于是这玩意就是一个 q-binomial,总贡献得到

12k1i=1r(2k2i1)xri=1n+ir2ni+112i1

转化形式求和得到

r=0ni=1r(2k2i)×[nnr]2

可以发现这两种方式得到的答案是相同的。

直接模拟计算即可。预处理后单次计算答案复杂度 O(k)

code
#include <bits/stdc++.h>
#define rep(i,a,b) for (register int (i) = (a); (i) <= (b); ++(i))
#define pre(i,a,b) for (register int (i) = (a); (i) >= (b); --(i))
using namespace std;
const int N = 1e7 + 10, K = 1<<15, mod = 998244353, inv2 = 499122177;
int T, n, k, x, pw[N], upw[K + 10], fac[N], ifac[N];

typedef long long ll; typedef __int128 lll;
struct FastMod { int m; ll b; void init(int _m) { m = _m; b = ((lll)1<<64) / m; } int operator() (ll a) {ll q = ((lll)a * b) >> 64; a -= q * m; if (a >= m) a -= m; return a; } } Mod;
int add(int a, int b) { return (a += b) >= mod ? a - mod : a; } int mul(int a, int b) { return Mod(1ll * a * b); } template <typename ...Args> int mul(int a, Args ...b) { return mul(a, mul(b...)); }

int qp(int a, int b) {
    int ret = 1;
    while (b) {
        if (b & 1) ret = mul(ret, a);
        a = mul(a, a);
        b >>= 1;
    } return ret;
}

void init(int n = N - 10) {
    Mod.init(mod);
    pw[0] = upw[0] = fac[0] = ifac[0] = 1;
    rep(i,1,n) pw[i] = add(pw[i-1], pw[i-1]);
    upw[1] = pw[K];
    rep(i,2,K) upw[i] = mul(upw[i-1], upw[1]);
    rep(i,1,n) fac[i] = mul(fac[i-1], mod + 1 - pw[i]);
    ifac[n] = qp(fac[n], mod - 2);
    pre(i,n-1,1) ifac[i] = mul(ifac[i+1], mod + 1 - pw[i+1]);
}

int qw(int x) {return mul(upw[x >> 15], pw[x & 32767]);}
int C(int n, int m) {
    if (m < 0 or n < m) return 0;
    return mul(fac[n], ifac[m], ifac[n-m]);
}

int solve() {
    if (x == 0) {
        int ret = 1, tmp = min(n-1, k); 
        if (n > k) return 0;
        rep(i,0,n-1) ret = mul(ret, add(pw[k], mod - pw[i]));
        return ret;
    } else {
        int tmp = 1, ans = 0, c = qw(n);
        rep(i,0,k-1) {
            tmp = mul(tmp, add(c, mod - pw[i]));
            ans = add(ans, mul(tmp, C(k-1, i)));
        } ans = add(qw(1ll * n * k % (mod - 1)), mod - ans);
        return ans;
    }
}

signed main() {
    ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
    cin >> T; 
    init();
    while (T--) {
        cin >> n >> k >> x;
        cout << solve() << '\n';
    }
}
posted @   joke3579  阅读(147)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示