peiwenjun's blog 没有知识的荒原

CCPC2021威海 CF103428B Subset 题解

题目描述

给定 \(n,m,b\) ,求满足以下条件的序列 \(a\) 个数,对 \(998244353\) 取模:

  • \(0\le a_1\lt\cdots\lt a_m\le n\)
  • \(\text{popcount}(\bigoplus_{i=1}^na_i)=d\)

数据范围

  • \(0\le n\le 10^9,1\le m\le 5\cdot 10^3,0\le d\le 30\)

时间限制 \(\texttt{4s}\) ,空间限制 \(\texttt{256MB}\)

分析

先假设这 \(m\) 个数是无序的,最后除以 \(m!\) 即可。

发现 \(a_i\) 互不相同这个条件很难处理,考虑容斥。

\(f(m)\) 表示在 \([0,n]\) 中选 \(m\) 个数,异或和恰有 \(d\)\(1\) 的方案数。

\(g(m)\) 表示在 \([0,n]\) 中选 \(m\) 个互不相同的数,异或和恰有 \(d\)\(1\) 的方案数。


先考虑如何计算 \(f(0),\cdots,f(m)\)

\(n\gets n+1\) ,将值域区间变为左闭右开。

注意到异或有良好的性质: \(\forall 0\le x\lt 2^k\) ,在 \([0,2^k)\) 中随机一个数异或 \(x\) ,得到的结果仍在 \([0,2^k)\) 等概率随机。

枚举 \(i\) 为出现没有顶住上界的数的最高位,这一位有 \(j\) 个数为 \(1\) ,则 \(0\le j\lt m\)

  • 高于 \(i\) 的位对 popcount 的贡献显然为定值,记为 cnt
  • \(i\) 位对 popcount 的贡献是 j&1
  • 低于 \(i\) 的位取值在 \([0,2^i)\) 中等概率随机,有 \(\frac 1{2^i}\binom i{d-cnt-(j\&1)}\) 的概率符合要求。

\[f(m)=\sum_{n\&2^i\neq 0}\sum_{j=0}^{m-1}\frac 1{2^i}\binom i{d-cnt-(j\&1)}\binom mj(n\bmod 2^i)^j(2^i)^{m-j}\\ \]

\(x=n\bmod 2^i\) ,对 \(j\) 的奇偶性分别化简:

\[f(m)=\sum_{n\&2^i\neq 0}\frac 1{2^i}\big(\binom i{d-cnt}\frac{(2^i+x)^m+(2^i-x)^m}2+\binom i{d-cnt-1}\frac{(2^i+x)^m-(2^i-x)^m}2-\epsilon\big)\\ \]

其中 \(\epsilon\) 为带入 \(j=m\) 得到的结果,从而可以在 \(\mathcal O(\log n)\) 时间内计算单个 \(f(m)\) 的值。


再考虑如何通过 \(f\) 反解出 \(g\)

构造一个序列映射:将长为 \(i\) 的序列 \(a_1,\cdots,a_i\) 依次加入 \(b\) ,如果 \(b\) 中有与之相同的元素,则将它们消去,否则将新加入的数放到 \(b\) 的末尾。

\(h_{i,j}\) 表示长为 \(i\) 的可重数列,映射到长为 \(j\) 的不可重数列的方案数。

注意序列 \(a,b\) 的元素有标号,即交换两个位置的元素,算作不同的序列。

转移方程枚举新加的数是否已经在 \(b\) 中出现:

\[h_{i,j}=(n-j+1)\cdot h_{i-1,j-1}+(j+1)\cdot h_{i-1,j+1}\\ \]

显然 \(b\)\(A_n^j\) 种,并且每个 \(b\) 对应 \(a\) 的个数相同。

于是可以用 \(g\) 表示 \(f\)

\[f_i=\sum_{j=0}^i\frac{h_{i,j}}{A_n^j}\cdot g_j \]

进而反解出 \(g\)

\[g_i=f_i-\sum_{j=0}^{i-1}\frac{h_{i,j}}{A_n^j}\cdot g_j \]

代码实现时,重定义 \(h'_{i,j}=\frac{h_{i,j}}{A_n^j}\) ,并据此推导 \(h'\) 的转移方程,可以避开计算排列数的代价。

时间复杂度 \(\mathcal O(m^2+m\log n)\)

#include<bits/stdc++.h>
using namespace std;
const int maxn=5005,mod=998244353,inv2=(mod+1)/2;
int d,m,n;
int f[maxn],g[maxn],inv[maxn];
int h[maxn][maxn];
int qpow(int a,int k)
{
    int res=1;
    while(k)
    {
        if(k&1) res=1ll*res*a%mod;
        a=1ll*a*a%mod,k>>=1;
    }
    return res;
}
int c(int n,int m)
{
    if(m<0) return 0;
    int res=1;
    for(int i=1;i<=m;i++) res=res*(n+1ll-i)%mod;
    return 1ll*res*inv[m]%mod;
}
int main()
{
    scanf("%d%d%d",&n,&m,&d),n++,inv[0]=1;
    for(int i=1;i<=max(m,30);i++) inv[i]=1ll*inv[i-1]*qpow(i,mod-2)%mod;
    f[0]=!d;
    for(int i=0;i<=29;i++)
    {
        if(!(n>>i&1)) continue;
        int x=n&((1<<i)-1),cnt=__builtin_popcount(n>>(i+1)),inv=qpow(1<<i,mod-2);
        int a[2]={c(i,d),c(i,d-cnt)},b[2]={c(i,d-1),c(i,d-cnt-1)};
        for(int j=1,u=1,v=1,w=1;j<=m;j++)
        {
            u=1ll*u*((1<<i)+x)%mod,v=1ll*v*((1<<i)-x)%mod,w=1ll*w*x%mod;
            int e=j&1;
            f[j]=(f[j]+((1ll*a[e]*(u+v)+1ll*b[e]*(u-v))%mod*inv2-1ll*(!e?a[e]:b[e])*w)%mod*inv)%mod;
        }
    }
    g[0]=f[0],h[0][0]=1;
    for(int i=1;i<=m;i++)
    {
        g[i]=f[i];
        for(int j=0;j<=i;j++)
        {
            h[i][j]=((j?h[i-1][j-1]:0)+(j+1ll)*(n-j)%mod*h[i-1][j+1])%mod;
            if(j!=i) g[i]=(g[i]-1ll*h[i][j]*g[j])%mod;
        }
    }
    printf("%lld\n",1ll*(g[m]+mod)*inv[m]%mod);
    return 0;
}

posted on 2023-04-28 14:12  peiwenjun  阅读(22)  评论(0)    收藏  举报

导航