先膜一發 shadowice1984的題解,太神了!
題意簡述
你有一個 ,表示你的二叉樹將要有 個節點。然後每次你的樹會等概率選擇某個點的還沒長過的兒子,在這裏長一個兒子。容易證明,這樣有 種方案。
(第一次有一種方案,第二次兩種,第三次三種…一共就是 種)
然後你要輸出樹上路徑和的期望值乘以 後 的值。
給定,滿足:
思路
考慮每條邊的貢獻:如果樹是確定的,那麼從 連向 父親的邊,的貢獻是 。其中 表示 的子樹大小。枚舉 ,把這個式子加起來就是答案了。
然後我們現在樹是不確定的…注意到 ,所以我們考慮再加一維枚舉 。關鍵就是,如何計算點 的子樹裏有 個點的樹的方案數?
考慮點 子樹內,有 種生成的形態。然後我們選擇哪些點呢?這個方案數有 種。
這一部分答案爲 。
考慮點 子樹外。在樹生成到 之前,有 種方案。然後我們後面的 個點,還要保證不能放到 子樹內。然後第一次有 種方案,第二次 種,第 次有 種方案。一共就是 種方案。
這一部分答案爲
於是,枚舉 後,總共的答案就是
從 到 , 從 到 ,求和即可
代碼
#include <bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
#define N 2333
#define int long long
#define F(i,l,r) for(int i=l;i<=r;++i)
#define D(i,r,l) for(int i=r;i>=l;--i)
#define Fs(i,l,r,c) for(int i=l;i<=r;c)
#define Ds(i,r,l,c) for(int i=r;i>=l;c)
#define MEM(x,a) memset(x,a,sizeof(x))
#define FK(x) MEM(x,0)
#define Tra(i,u) for(int i=G.Start(u),v=G.To(i);~i;i=G.Next(i),v=G.To(i))
#define p_b push_back
#define sz(a) ((int)a.size())
#define iter(a,p) (a.begin()+p)
int I()
{
int x=0;char c=getchar();int f=1;
while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
return (x=(f==1)?x:-x);
}
void Rd(int cnt,...)
{
va_list args; va_start(args,cnt);
F(i,1,cnt) {int* x=va_arg(args,int*);(*x)=I();}
va_end(args);
}
// ==================== 預處理階乘,組合數
int C[N][N],fac[N];
int mod;
void Init()
{
int n=2000;
C[0][0]=fac[0]=1;
F(i,1,n)
{
fac[i]=fac[i-1]*i%mod;
C[i][0]=1;
F(j,1,i) C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
}
// ====================
int n;
void Input()
{
Rd(2,&n,&mod);
}
int InSubtree(int i,int size) {return C[n-i][size-1]*fac[size]%mod;}
// 在子樹內的方案
int OutSubtree(int i,int size) {return fac[n-size-1]%mod*i%mod*(i-1)%mod;}
// 在子樹外的方案
void Soviet()
{
int ans=0;
F(i,2,n) F(size,1,n-i+1)
{
int cur=size*(n-size)%mod;
cur=cur*InSubtree(i,size)%mod;
cur=cur*OutSubtree(i,size)%mod;
ans=(ans+cur)%mod;
}
printf("%lld\n",ans);
}
#define Flan void
Flan IsMyWife()
{
Input();
Init();
Soviet();
}
#undef int //long long
}
int main()
{
Flandre_Scarlet::IsMyWife();
getchar();getchar();
return 0;
}