P12495 链覆盖 题解
集训队互测出现大量不可做计数 我被吓死了 余生在阿巴阿巴中度过……
首先你可以发现!想要解决原问题我们只需要将树进行长链剖分然后贪心地选前
那么要想解决计数版问题,我们可以考虑给定长剖完的链长集合后,有多少棵树可以被剖成给定的集合。首先我们要规范一下长剖的过程,如果一个节点的多个儿子深度相等,我们要选择 链底节点编号最小 的儿子。那么我们就要考虑有多少棵树,满足它被长剖之后被分为了
如图所示,我们只需要为每个不为
推到这里之后,这题的关键部分就已经过去了。题目中的
不过,本题的 dp 其实比较复杂,想要的答案和 dp 过程之间的相性并不好。“前
最后需要注意答案矩阵的最后一列和别的列不太一样,由于这一列的
#include<bits/stdc++.h>
#define int long long
#define FSIZ 407693
#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)
#define cond(a,b)((a)?(b):0)
using namespace std;
int fac[FSIZ],ifac[FSIZ],inv[FSIZ];
int n,mod,dp1[304][304][304],dp2[304][304][304];
int C(int n1,int m1){
if(m1<0||m1>n1)return 0;
return fac[n1]*ifac[m1]%mod*ifac[n1-m1]%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 res[304][304][304],ans[350][350];
signed main(){
scanf("%lld%lld",&n,&mod);
if(n==1){printf("1");return 0;}
fac[0]=1;for(int i=1;i<=FSIZ-1;i++)fac[i]=fac[i-1]*i%mod;
ifac[FSIZ-1]=qpow(fac[FSIZ-1],mod-2);for(int i=FSIZ-1;i>=1;i--)ifac[i-1]=ifac[i]*i%mod;
inv[0]=0;for(int i=1;i<=FSIZ-1;i++)inv[i]=ifac[i]*fac[i-1]%mod;
for(int i=0;i<=n;i++){
res[i][0][0]=1;
for(int j=1;j<=n-i;j++){
for(int h=0;h<=j;h++){
if(h>0)add(res[i][j][h],res[i][j-1][h-1]);
add(res[i][j][h],res[i][j-1][h]*(i+h)%mod);
}
}
}
for(int i=n;i>=1;i--)dp1[i][1][i]=1;
for(int i=n-1;i>=1;i--){
for(int j=1;j<=n;j++){
for(int h1=j+1;h1<=n;h1++){
for(int h2=(i*h1)+(h1-j);h2<=n;h2++){
//printf("%lld %lld %lld %lld\n",i,j,h1,h2);
add(dp1[i][h1][h2],dp1[i+1][h1-j][h2-i*j]*res[h2-(i*h1)-(h1-j)][h1][h1-j]%mod);
}
}
}
for(int h1=1;h1<=n;h1++){
for(int h2=1;h2<=n;h2++){
add(dp1[i][h1][h2],dp1[i+1][h1][h2]);
}
}
}
for(int i=1;i<n;i++)dp2[1][i][n]=fac[n-1]*ifac[i]%mod;
for(int i=1;i<n;i++){
for(int h1=1;h1<=n;h1++){
for(int h2=h1*i;h2<=n;h2++){
for(int j=1;h1-j>=0&&h2-i*j>=0;j++){
if(h2-i*h1-(h1-j)>=0)add(dp2[i+1][h1-j][h2-i*j],dp2[i][h1][h2]*res[h2-i*h1-(h1-j)][h1][h1-j]%mod);
}
}
}
for(int h1=1;h1<=n;h1++){
for(int h2=1;h2<=n;h2++){
add(dp2[i+1][h1][h2],dp2[i][h1][h2]);
}
}
}
for(int i=2;i<=n;i++){
for(int j=1;j<=n;j++){
for(int h1=1;h1<=n-j;h1++){
for(int h2=h1*i;h2<=n-(i-1)*j;h2++){
for(int j2=1;j2<=j;j2++){
add(ans[h1+j2][h2+j2*(i-1)],dp1[i][h1][h2]*res[h2-h1-(i-1)*h1][h1+j][h1]%mod*dp2[i-1][h1+j][h2+(i-1)*j]%mod);
}
}
}
}
}
for(int i=2;i<=n;i++){
add(ans[1][i],dp2[i][1][i]);
}
for(int i=2;i<=n;i++){
add(ans[i][n],ans[i-1][n]);
}
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
printf("%lld ",ans[i][j]);
}
printf("\n");
}
}