P5401 [CTS2019]珍珠
至少 \(k\) 对数相同,感觉可以二项式反演,发现不是很好搞。
草为啥这个是算“至少”啊,感觉之前的题都是算“恰好”。
然后想着想着睡着了,天天数数真累啊
睡醒之后改成了,出现次数为奇数的数 \(\le n-2m\)
发现还是不会算,然后改回去了。。。我在干什么啊,真·数数学傻
稍微想了想也没别的方法。
那么至少 \(k\) 对数相同的情况就必须得攻克。
忽然发现根本不能算 \(k\) 对数相同,而应该算 \(k\) 种数出现次数为偶数,而偶数的数量不少于 \(D-(n-2m)\) 就一定有解。(就是奇数的数量 \(\le n-2m\))
这个东西没有标号感觉可恶。。。然后就想到了 \(\rm EGF\)。
加一下我们就有了偶数的 \(\rm EGF\)
发现可以算了,令 \(f(k)\) 表示 \(k\) 种数是偶数的情况。
只不过算重是显然的/kk,但是没关系,二项式反演就好了。
令 \(g(k)\) 表示恰好 \(k\) 种数出现了偶数次的方案数
发现我们把 \(f\) 求出来,\(g\) 就能求出来,由于 \(D\) 很小,可以枚举所有 \(i\in [D-(n-2m),D]\) 的 \(g(i)\) ,加起来就是答案了。
看到了一丝希望,开始搞 \(f\) 。
40min later:希 望 破 灭 。
woc这个是啥玩意啊。
不想看题解,自己又不会。。。算了,稍微休息下再给一小时。
然后去看了rqy的WC游记。
rqy:T1 大计数,多项式 exp,成功把我推到 rk1,吊打集训队水平。
不能再颓废下去了,不然一生都不可能有rqy的水平了!
既然没啥巧妙的想法就暴力试试吧。
那个 \(\dfrac{1}{2^k}\) 先提出来。
前面的系数直接忽略,毕竟最后再乘上去是一个可以接受的复杂度。
所以现在要算的是这个东西:
之所以说是暴力,因为,我 想 把 前 面 那 个 \(k\) 次 方 二 项 式 定 理 展 开。貌似也不怎么暴力
因为
现在需要的是对于每一个 \(k\in [0,D]\) 算出 \([x^n]\) 。
惊奇的发现居然是直接相加的,也就是说直接把 \([x^n]\) 提出来,不用管其余的次数了(之前一直没法下手就是因为还有别的次数在,要卷积 \(D\) 次)
感觉这个东西可以直接卷积了
由于 \(\rm EGF\) 最后还得乘 \(n!\) ,直接消掉算了,记得最后别多乘。
直接卷积即可。
特别鸣谢:rqy,您的游记鼓舞了我,终于切了这道CTS数数题
实现的时候注意统计答案的时候,\(D-(n-2m)\) 要和 \(0\) 取 \(\max\) ,还好出题人只卡了 \(4\) 分,如果是考场上要完蛋啊
以下部分为自己推卷积,可以跳过不看。
主要就是 \(f\to g\) 的部分了。
差卷积不能每次都这么推啊,还是找个通式记下来算了。
令 \(C_i=B_{n-i}\)
所以 \(g(k)=\dfrac{1}{k!}[x^{n+k}](A*C)\)
// Problem: P5401 [CTS2019]珍珠
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P5401
// Memory Limit: 500 MB
// Time Limit: 1000 ms
#include<bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define mkp(x,y) make_pair(x,y)
#define pb(x) push_back(x)
#define sz(v) (int)v.size()
typedef long long LL;
typedef double db;
template<class T>bool ckmax(T&x,T y){return x<y?x=y,1:0;}
template<class T>bool ckmin(T&x,T y){return x>y?x=y,1:0;}
#define rep(i,x,y) for(int i=x,i##end=y;i<=i##end;++i)
#define per(i,x,y) for(int i=x,i##end=y;i>=i##end;--i)
inline int read(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
return f?x:-x;
}
const int N=100005;
const int M=N<<2;
#define mod 998244353
namespace math{
int fac[N],ifc[N],inv[N];
inline int qpow(int n,int k){int res=1;for(;k;k>>=1,n=1ll*n*n%mod)if(k&1)res=1ll*n*res%mod;return res;}
inline int comb(int n,int m){return n<m?0:1ll*fac[n]*ifc[m]%mod*ifc[n-m]%mod;}
void initmath(const int&n=N-1){
fac[0]=1;for(int i=1;i<=n;++i)fac[i]=1ll*fac[i-1]*i%mod;
ifc[n]=qpow(fac[n],mod-2);for(int i=n-1;i>=0;--i)ifc[i]=1ll*(i+1)*ifc[i+1]%mod;
inv[1]=1;for(int i=2;i<=n;++i)inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
}
}
using namespace math;
namespace poly{
int rev[M],lg,lim;
void init_poly(const int&n){
for(lg=0,lim=1;lim<n;++lg,lim<<=1);
for(int i=0;i<lim;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1));
}
void NTT(int*a,int op){
for(int i=0;i<lim;++i)
if(i>rev[i])swap(a[i],a[rev[i]]);
const int g=op?3:inv[3];
for(int i=1;i<lim;i<<=1){
const int wn=qpow(g,(mod-1)/(i<<1));
for(int j=0;j<lim;j+=i<<1){
int w0=1;
for(int k=0;k<i;++k,w0=1ll*w0*wn%mod){
const int X=a[j+k],Y=1ll*w0*a[i+j+k]%mod;
a[j+k]=(X+Y)%mod,a[i+j+k]=(X-Y+mod)%mod;
}
}
}
if(op)return;const int ilim=qpow(lim,mod-2);
for(int i=0;i<lim;++i)a[i]=1ll*a[i]*ilim%mod;
}
#define clr(a,n) memset(a,0,sizeof(int)*(n))
#define cpy(a,b,n) memcpy(a,b,sizeof(int)*(n))
void poly_mul(int*f,int*g,int*ans,int n,int m){
static int A[M],B[M];init_poly(n+m);
cpy(A,f,n),clr(A+n,lim-n),NTT(A,1);
cpy(B,g,m),clr(B+m,lim-m),NTT(B,1);
for(int i=0;i<lim;++i)ans[i]=1ll*A[i]*B[i]%mod;
NTT(ans,0);
}
}
int D,n,m,f[M],g[M],ans;
signed main(){
initmath();
D=read(),n=read(),m=read();
for(int i=0;i<=D;++i)f[i]=ifc[i];
for(int i=0;i<=D;++i)g[i]=(1ll*qpow(D-2*i,n)*ifc[i]%mod+mod)%mod;
poly::poly_mul(f,g,f,D+1,D+1);
for(int i=0;i<=D;++i)f[i]=1ll*f[i]*fac[i]%mod;
for(int i=0,j=1;i<=D;++i,j=1ll*j*inv[2]%mod)f[i]=1ll*comb(D,i)*j%mod*f[i]%mod;
for(int i=0;i<=D;++i)f[i]=1ll*f[i]*fac[i]%mod;
for(int i=0;i<=D;++i)g[i]=i&1?mod-ifc[i]:ifc[i];
reverse(g,g+D+1);
poly::poly_mul(f,g,f,D+1,D+1);
for(int i=0;i<=D;++i)g[i]=1ll*ifc[i]*f[D+i]%mod;
for(int i=max(0,D-(n-2*m));i<=D;++i)ans=(ans+g[i])%mod;
printf("%d\n",ans);
return 0;
}