题解:P10005 [集训队互测 2023] 基础寄术练习题
nullqtr_pwp · · 题解
搬运下官方题解。
现有的另一篇题解的做法是将"前缀和积"映射到树的拓扑序数上,两种做法的第一步都是将它转化掉。
以下的
k=1
分子上的东西我们通常可以通过累加的技巧解决,但是这个题的分母非常神秘,是前缀和积的倒数,因此考虑优先解决分母上的问题。
考虑一个组合模型:有一个序列,有
bonus:将这个问题高维化之后,就是【CTS2019】随机立方体 的做法,解决方法是相同的。
注意到这种形式就是前缀和积倒数,考虑进一步拓展这个思想,对于一个
所以
相当于每个序列都有
考虑最终一起统计的时候,每个排列的
正难则反,考虑容斥钦定一个集合
逐个理解即可,除以总个数就是多重集组合数之后累计进答案,这个分式化简后为
考虑整体做法,使用 DP 加速枚举子集。设
时间复杂度
namespace A{
int f[105][105];
void solve(){
f[0][0]=1;
F(i,1,m) F(j,0,min(i,n)) f[i][j]=add(f[i-1][j],(j==0)?0:(1ll*f[i-1][j-1]*inv[i]%mod));
printf("%d",f[m][n]);
}
}
namespace B{
int f[2][105][10005][2];
void solve(){
f[0][0][0][0]=1;
int ans=0;
F(i,1,m){
const int cur=i&1,lst=cur^1;
F(j,0,n-1)F(k,0,i*m)F(l,0,1){
f[cur][j][k][l]=f[lst][j][k][l];
if(j>0) inc(f[cur][j][k][l],1ll*inv[i]*f[lst][j-1][k][l]%mod);
if(j>0&&k>=i) dec(f[cur][j][k][l],1ll*inv[i]*f[lst][j-1][k-i][l]%mod);
if(l>0&&k>=i) inc(f[cur][j][k][l],1ll*i*f[lst][j][k-i][0]%mod);
}
}
F(i,1,m*m) inc(ans,1ll*inv[i]*f[m&1][n-1][i][1]%mod);
printf("%d",ans);
}
}
void solve(){
n=read(),m=read(),k=read(),mod=read();
init(maxn-3);
if(k==1) return A::solve(),void();
if(k==2) return B::solve(),void();
}