YbtOJ-森林之和【dp】
生活随笔
收集整理的這篇文章主要介紹了
YbtOJ-森林之和【dp】
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
正題
題目大意
一個節點的權值定義為它度數的平方,求所有nnn個點的有標號森林的所有節點權值和。
1≤n,T≤5×1031\leq n,T\leq 5\times 10^31≤n,T≤5×103
解題思路
首先因為所有節點本質相同,所以我們可以只考慮一個節點所有情況下的權值和。
然后考慮這個平方和怎么做,我們可以視為指定一個節點連出兩顆子樹的方案(可以相同)。
那么考慮這個怎么做,首先我們需要處理出nnn個節點有根樹和無根樹的數組r,fr,fr,f。
然后我們要考慮怎么統計除了指定子樹以外的方案,首先我們需要處理出nnn個點的森林個數sns_nsn?。
我們可以考慮每次枚舉新加入的樹的大小,但是要指定這個節點編號最小的節點編號必須是111(以防相同的子樹算重),那么有
sn=∑i=1nsn?ifi(n?1i?1)s_n=\sum_{i=1}^ns_{n-i}f_{i}\binom{n-1}{i-1}sn?=i=1∑n?sn?i?fi?(i?1n?1?)
然后還要算上非指定的子樹中和111號點聯通的其他節點的方案,那么有
gn=∑i=0nsiri+1(ni)g_n=\sum_{i=0}^ns_ir_{i+1}\binom{n}{i}gn?=i=0∑n?si?ri+1?(in?)
至于指定子樹的話,我們枚舉指定子樹的大小轉移就好了。
時間復雜度:O(n2+T)O(n^2+T)O(n2+T)
code
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const ll N=5100; ll T,P,C[N][N],f[N],r[N],g[N],s[N],d[N],ans[N]; ll power(ll x,ll b){ll ans=1;while(b){if(b&1)ans=ans*x%P;x=x*x%P;b>>=1;}return ans; } signed main() {freopen("forest.in","r",stdin);freopen("forest.out","w",stdout);scanf("%lld%lld",&T,&P);C[0][0]=1;for(ll i=1;i<N;i++)for(ll j=0;j<=i;j++)C[i][j]=(C[i-1][j]+(j?C[i-1][j-1]:0))%P;f[0]=f[1]=r[0]=r[1]=g[0]=s[0]=1;for(ll i=2;i<N;i++) f[i]=power(i,i-2),r[i]=f[i]*i%P;for(ll i=1;i<N;i++)for(ll j=0;j<i;j++)(s[i]+=s[j]*f[i-j]%P*C[i-1][j]%P)%=P;for(ll i=1;i<N;i++){for(ll j=0;j<=i;j++)(g[i]+=s[j]*f[i-j+1]%P*C[i][j]%P)%=P;for(ll j=1;j<i;j++)(ans[i]+=r[j]*g[i-j-1]%P*C[i-1][j]%P)%=P;}for(ll i=1;i<N;i++){d[i]=ans[i+1]; // for(ll j=1;j<=i;j++) // (d[i]+=r[j]*g[i-j]%P*C[i][j]%P)%=P;for(ll j=1;j<i-1;j++)(ans[i]+=d[j]*r[i-j-1]%P*C[i-1][j]%P)%=P;}while(T--){ll n;scanf("%lld",&n);printf("%lld\n",ans[n]*n%P);}return 0; } 創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的YbtOJ-森林之和【dp】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 电脑配置3500左右怎么选(电脑配置35
- 下一篇: Loj#2460-「POI2010」桥B