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)}\) 的概率符合要求。
记 \(x=n\bmod 2^i\) ,对 \(j\) 的奇偶性分别化简:
其中 \(\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\) 中出现:
显然 \(b\) 有 \(A_n^j\) 种,并且每个 \(b\) 对应 \(a\) 的个数相同。
于是可以用 \(g\) 表示 \(f\) :
进而反解出 \(g\) :
代码实现时,重定义 \(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;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/17361999.html
浙公网安备 33010602011771号