Description
SPOJ PT07D
1:求n個點有標號無根樹個數
2:求n個點有標號有根樹個數
3:求n個點無標號有根樹個數
4:求n個點無標號無根樹個數
Solution
我太菜了現在纔來做這道題
1和2很好求,略去
設fn爲n個點無標號有根樹,考慮刪去根得到了一些無標號有根樹
因爲是無標號,考慮一種大小爲k的子樹,貢獻爲
且有fk種,所以
兩邊取ln
兩邊求導
兩邊取[x^n],
注意到,即
後面的東西可以提前記一下然後O(n^2)遞推
當然也可以用分治NTT做到O(n log^2 n)
無標號無根樹
首先先求出有根樹fn,設無根樹爲hn,考慮如何唯一表示一棵樹
容易想到重心,那麼我們把根不是重心的全部減掉,不是重心就是有一個子樹大小>n/2
注意當n爲偶數時有兩個重心,所以多減了兩邊子樹相同的情況,要加回來
Code
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int N=1e3+5;
int n,k,p,f[N],g[N];
int pwr(int x,int y) {
if (y<0) y+=p-1;
int z=1;x%=p;
for(;y;y>>=1,x=(ll)x*x%p)
if (y&1) z=(ll)z*x%p;
return z;
}
int solve_f(int n) {
f[1]=1;fo(j,1,n) g[j]=1;
fo(i,2,n) {
f[i]=0;
fo(j,1,i-1) f[i]=(f[i]+(ll)f[j]*g[i-j]%p)%p;
f[i]=(ll)f[i]*pwr(i-1,p-2)%p;
for(int j=i;j<=n;j+=i) g[j]=(g[j]+(ll)i*f[i]%p)%p;
}
return f[n];
}
int solve_h(int n) {
int ans=solve_f(n);
fo(i,1,n/2) ans=(ans-(ll)f[i]*f[n-i]%p+p)%p;
if (!(n&1)) {
ans=(ans+(ll)f[n/2]*f[n/2]%p)%p;
ans=(ans-(ll)f[n/2]*(f[n/2]-1)%p*pwr(2,p-2)%p+p)%p;
}
return ans;
}
int main() {
while (scanf("%d%d%d",&k,&n,&p)!=EOF) {
if (k==1) printf("%d\n",pwr(n,n-2));
if (k==2) printf("%d\n",pwr(n,n-1));
if (k==3) printf("%d\n",solve_f(n));
if (k==4) printf("%d\n",solve_h(n));
}
return 0;
}