P10005 基础寄术练习题 题解
首先,这题要求的是前缀和的积的倒数的和,你知道淘米神的树吧?没错,和这题没什么关系(
但这启发我们考虑构造一棵树,使得最后答案和某棵树的拓扑序数挂钩。我们构造一棵“右偏树”,每个点只有最右方的儿子不为叶子,深度为
然后考虑
upd:哦好吧,刚才我发现其实它和猎人杀的关系还是很大的,基本是一个容斥思路,原来只有我做那题没用容斥(
但这启发我们进行类似的转化,把这些置换环想象为一些体积不同的人,然后随机枪毙他们,每人每回合被枪毙概率和其体积成正比,我们要求的就是最后被枪毙的人的期望体积。你想到了什么?min-max 容斥!最后被枪毙的人的期望体积不好求,但我们可以求某集合里第一个被枪毙的人的期望体积啊!那么我们在前面 dp 的基础上,额外记录一维,表示当前被 min-max 容斥枚举的集合的总体积,然后此题就做完了!
时间复杂度
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
#define int long long
#define add(a,b) (a+=(b),a>=mod?a-=mod:0)
#define neg(x) ((x)&1?mod-1:1)
#define Q(a,b) C((a)+(b)-1,(b)-1)
using namespace std;
int inv[407693],mod;
inline int qpow(int n1,int n2){
int n3=n1,n4=1;
while(n2){
if(n2&1)n4*=n3,n4%=mod;
n3*=n3,n3%=mod;n2>>=1;
}return n4;
}
inline int mut(initializer_list<int> arg){
int ret=1;
for(auto i:arg)ret*=i,ret%=mod;
return ret;
}
int n,m,k,dp[300][300],pd[2][101][5340],pe[2][101][5340];
signed main(){
scanf("%lld%lld%lld%lld",&n,&m,&k,&mod);
for(int i=1;i<=9876;i++)inv[i]=qpow(i,mod-2);
if(k==1){
dp[0][0]=1;
for(int i=1;i<=m;i++){
for(int j=0;j<=i;j++){
if(j>0)dp[i][j]+=dp[i-1][j-1]*inv[i]%mod;
dp[i][j]+=dp[i-1][j];
dp[i][j]%=mod;
}
}
printf("%lld",dp[m][n]);
}
else{
pd[0][0][0]=mod-1;
for(int i=1;i<=m;i++){
for(int j=0;j<=i;j++){
int r=(i+i-j)*j/2;
for(int h=0;h<=r;h++){
pd[~i&1][j][h]%=mod;
pe[~i&1][j][h]%=mod;
pd[i&1][j][h]+=pd[~i&1][j][h];
pe[i&1][j][h]+=pe[~i&1][j][h];
pd[i&1][j+1][h]+=pd[~i&1][j][h]*inv[i]%mod;
pe[i&1][j+1][h]+=pe[~i&1][j][h]*inv[i]%mod;
pd[i&1][j+1][h+i]+=(mod-pd[~i&1][j][h])*inv[i]%mod;
pe[i&1][j+1][h+i]+=(mod+mod-pe[~i&1][j][h]-pd[~i&1][j][h]*i%mod*i%mod)*inv[i]%mod;
pd[~i&1][j][h]=0;
pe[~i&1][j][h]=0;
}
}
}
int ans=0;
for(int i=1;i<=(m)*(m+1)/2;i++)ans+=pe[m&1][n][i]*inv[i],ans%=mod;
printf("%lld",ans);
}
}