1. 程式人生 > 實用技巧 >2020牛客暑期多校訓練營(第七場)I-Valuable Forests

2020牛客暑期多校訓練營(第七場)I-Valuable Forests

I-Valuable Forests

題意

定義一個森林的權值為這個森林所有點的度數和,計算所有\(n\)個點的森林的權值和。

分析

首先我們需要學習一下 prufer 序列,得到一個公式:

一個 \(n\) 個點 \(m\) 條邊的帶標號無向圖有 \(k\) 個連通塊。第 \(i\) 個連通塊的大小為 \(s_i\) 。新增 \(k-1\) 條邊使得整個圖連通的方案數為

\[n^{k-2} \cdot \prod_{i=1}^{s_i} \]

定義\(a[n]\)\(n\)個點的帶標號的樹的個數,由上述公式可知

\[a_i=i^{i-2} \]

定義\(b[n]\)\(n\)個點的帶標號的森林的個數,遞推求出\(b[n]\)

首先\(b_i\)包含\(a_i\),考慮向圖中加入點\(i\)\(j\)個點連成一顆\(j+1\)個點的樹的方案數為\(a_{j+1}\),剩餘\(i-j-1\)個點自成森林的方案數為\(b_{i-j-1}\),合起來就是\(a_{j+1} \cdot b_{i-j-1}\)

\[b_i=a_i+\sum_{j=0}^{i-2}\binom{i-1}{j} \cdot a_{j+1} \cdot b_{i-j-1} \]

定義\(c[n]\)為所有\(n\)個點的帶標號的樹的權值和,考慮\(i\)個點的樹中點\(i\)直接和\(j\)個點相連,剩餘\(i-j-1\)個點通過這\(j\)個點間接和點\(i\)

相連的方案數為\(D\),貢獻為\(j^2 \cdot D\),顯然我們是要連線\(i-1\)個連通塊,其中一個連通塊大小為\(j\)\(i-2\)個連通塊的大小為\(1\)\(D=(i-1)^{i-j-2}\cdot j\)。點\(i\)直接和剩餘\(i-1\)個點相連的答案要單獨算,有\(i\)個點,將總貢獻再乘個\(i\)

\[c_i=i\cdot ((i-1)^2+ \sum_{j=1}^{i-2}\binom{i-1}{j}\cdot j^3\cdot (i-1)^{i-j-2}) \]

定義\(d[n]\)為所有\(n\)個點的帶標號的森林的權值和,遞推求出\(d[n]\)

首先\(d_i\)

包含\(c_i\),和之前類似,考慮有\(j\)個點和\(i\)號點相連,那麼這個森林是由一個\(j+1\)個點的樹和\(i-j-1\)個點的森林組成,那這部分的貢獻就是 樹的貢獻乘上森林的方案數+森林的貢獻乘上樹的方案數 。

\[d_i=c_i+\sum_{j=0}^{i-2}\binom{i-1}{j}\cdot (c_{j+1}\cdot b_{i-j-1}+d_{i-j-1}\cdot a_{j+1}) \]

Code

#include<algorithm>
#include<iostream>
#include<cstring>
#include<iomanip>
#include<sstream>
#include<cstdio>
#include<string>
#include<vector>
#include<bitset>
#include<queue>
#include<cmath>
#include<stack>
#include<set>
#include<map>
#define rep(i,x,n) for(int i=x;i<=n;i++)
#define per(i,n,x) for(int i=n;i>=x;i--)
#define sz(a) int(a.size())
#define rson mid+1,r,p<<1|1
#define pii pair<int,int>
#define lson l,mid,p<<1
#define ll long long
#define pb push_back
#define mp make_pair
#define se second
#define fi first
using namespace std;
const double eps=1e-8;
const int N=1e5+10;
const int inf=1e9;
int T,p;
ll a[5010],b[5010],c[5010],d[5010],f[5010][5010],C[5010][5010];
void init(int n){
    rep(i,1,n){
        f[i][0]=1;
        rep(j,1,n) f[i][j]=f[i][j-1]*i%p;
    }
    C[0][0]=1;
    rep(i,1,n){
        C[i][0]=1;
        rep(j,1,i) C[i][j]=(C[i-1][j-1]+C[i-1][j])%p;
    }
    a[1]=1;
    rep(i,2,n) a[i]=f[i][i-2];
    rep(i,1,n){
        b[i]=a[i];
        rep(j,0,i-2) b[i]=(b[i]+a[j+1]*b[i-j-1]%p*C[i-1][j]%p)%p;
    }
    rep(i,1,n){
        c[i]=(i-1)*(i-1)%p;
        rep(j,1,i-2) c[i]=(c[i]+1ll*j*j*j%p*f[i-1][i-j-2]%p*C[i-1][j]%p)%p;
        c[i]=i*c[i]%p;
    }
    rep(i,1,n){
        d[i]=c[i];
        rep(j,0,i-2) d[i]=(d[i]+(c[j+1]*b[i-j-1]%p+a[j+1]*d[i-j-1]%p)%p*C[i-1][j]%p)%p;
    }
}
int main(){
    //ios::sync_with_stdio(false);
    //freopen("in","r",stdin);
    scanf("%d%d",&T,&p);
    init(5000);
    while(T--){
        int n;
        scanf("%d",&n);
        printf("%lld\n",d[n]);
    }
    return 0;
}