ARC139F Many Xor Optimization Problems【组合计数,q-analog】

给定正整数 \(n,m\),考虑所有 \(2^{nm}\) 个长为 \(n\) 且每个元素小于 \(2^m\) 的非负整数序列,求最大子序列异或和之和 \(\bmod 998244353\)

\(n,m\le 2.5\cdot 10^5\)


前置知识:q-二项式系数

参考了 wlzhouzhuan 的题解

枚举所有 \(\mathbb F_2^m\) 的线性子空间,其贡献为生成它的序列个数乘上最大元素。

引理. 长为 \(n\)\(\mathbb F_2^k\) 向量序列生成的线性空间即为 \(\mathbb F_2^k\) 的方案数为 \(\prod_{i=0}^{k-1}(2^n-2^i)\)

转置一下即为长为 \(k\)\(\mathbb F_2^n\) 向量序列线性无关的方案数。

所以枚举线性子空间的秩 \(k\),为了求出最大元素我们还需要枚举主元,设其从低到高分别为 \(a_1,\cdots,a_k\)

取线性子空间的标准基底(即每个主元都只有对应向量有值),最大元素即为所有基的异或和,所以最大元素的主元位置都为 \(1\),非主元各有 \(1/2\) 的概率为 \(1\),所以期望值为 \(2^{a_k}+\sum_{i=1}^k2^{a_i-1}-\frac 12\)。而主元对应的线性空间(也即标准基底)数量为 \(\prod_{i=1}^k2^{a_i-(i-1)}\),把这三项乘起来再求和就是答案。

接下来就是纯纯推柿子了。先把 \(2^{-\binom k2}\) 提出来,把期望值的三项拆开分别算。

先看最后一项:\([x^k]\prod_{i=0}^{m-1}(1+2^ix)\),熟练的选手都知道这是 \(2^{\binom k2}\binom mk_2\),不熟练的话对比一下递推公式(?

看中间一项:还要乘上 \(\sum_{i=1}^k2^{a_i}\),一个比较智慧的推法是总的 \(2^m-1\) 减去再选一个的贡献,也就是 \((2^m-1)2^{\binom k2}\binom mk_2-(k+1)2^{\binom{k+1}2}\binom m{k+1}_2\)

看第一项,还要乘上 \(2^{a_k}\),所以要枚举 \(i=a_k\),也就是下面这个柿子:

\[\begin{aligned} \text{Ans}&=\sum_{k=1}^{\min(n,m)}\left(2^\binom{k-1}2\color{#ff00ff}{\sum_{i=k-1}^{m-1}2^{2i}\binom{i}{k-1}_2}+\color{blue}{(2^{m-1}-1)}2^\binom k2\binom mk_2-(k+1)2^{\binom{k+1}2\color{blue}{-1}}\binom m{k+1}_2\right)2^{-\binom k2}\prod_{i=0}^{k-1}(2^n-2^i) \\ &=\frac 12\sum_{k=1}^{\min(n,m)}\left((2^{m+1}-1-2^{m-k})\binom{m}k_2-(k2^{k}+1)\binom m{k+1}_2\right)\prod_{i=0}^{k-1}(2^n-2^i) \end{aligned} \]

这个粉色柿子需要观察一下,实际上是一个经典的吸收恒等式 + 上指标求和。

\[\begin{aligned} \sum_{i=k}^{m-1}2^{2i}\binom ik_2&=2^{k-1}\sum_{i=k}^{m-1}(2^{i+1}-1)\cdot 2^{i-k}\binom{i}{k}_2+2^{k-1}\sum_{i=k}^{m-1}2^{i-k}\binom{i}{k}_2\\ &=(2^{2k}-2^{k-1})\sum_{i=k}^{m-1}2^{i-k}\binom{i+1}{k+1}_2+2^{k-1}\sum_{i=k}^{m-1}2^{i-k}\binom ik_2 \\ &=(2^{2k}-2^{k-1})\binom{m+1}{k+2}_2+2^{k-1}\binom{m}{k+1}_2 \end{aligned} \]

时间复杂度 \(\color{blue}{\mathcal O(m+\log n)}\)

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 250003, mod = 998244353;
void qmo(int &x){x += x >> 31 & mod;}
int ksm(int a, int b){
    int res = 1;
    for(;b;b >>= 1, a = (LL)a * a % mod)
        if(b & 1) res = (LL)res * a % mod;
    return res;
}
int n, m, pw[N], fac[N], inv[N], ans;
int C(int n, int m){
    if(m < 0 || n < m) return 0;
    return (LL)fac[n] * inv[m] % mod * inv[n-m] % mod;
}
int main(){
    ios::sync_with_stdio(false);
    cin >> n >> m; *fac = *pw = 1;
    for(int i = 1;i < N;++ i) qmo(pw[i] = (pw[i-1]<<1) - mod);
    for(int i = 1;i < N;++ i) fac[i] = fac[i-1] * (pw[i] - 1ll) % mod;
    inv[N-1] = ksm(fac[N-1], mod-2);
    for(int i = N-1;i;-- i) inv[i-1] = inv[i] * (pw[i] - 1ll) % mod;
    for(int k = 1, tmp = 1;k <= n && k <= m;++ k){
        tmp = tmp * ((LL)pw[n]+mod-pw[k-1]) % mod;
        ans = (ans + ((pw[m+1]-1ll+mod-pw[m-k])*C(m,k)+mod-((LL)k*pw[k]+1)%mod*C(m,k+1)%mod) % mod * tmp) % mod;
    }
    if(ans & 1) ans += mod;
    printf("%d\n", ans >> 1);
}
posted @ 2022-04-28 21:38  mizu164  阅读(361)  评论(0编辑  收藏  举报