[ARC087]F
来源:互联网 发布:淘宝win10激活码来源 编辑:程序博客网 时间:2024/05/12 14:58
题面
先考虑每条边
有两个重心:答案显然为
有一个重心:我们设去掉重心后的子树为
要让答案最大,那么不能存在有某只松鼠从
我们考虑容斥原理计算方案数:
其中
代码:
#include<iostream>#include<cstdio>#include<cstring>#define ll long longusing namespace std;const int maxn=5010;const int mod=1000000007;struct edge{ int t; edge *next;}*con[maxn];int n,sz[maxn];ll jie[maxn],inv[maxn],g[2][maxn];ll ksm(ll a,int b){ll r=1;for(;b;b>>=1){if(b&1) r=r*a%mod;a=a*a%mod;}return r;}ll A(int a,int b){return jie[a]*inv[a-b]%mod;}ll C(int a,int b){return A(a,b)*inv[b]%mod;}void ins(int x,int y){ edge *p=new edge; p->t=y; p->next=con[x]; con[x]=p;}void getroot(int v,int fa,int &root){ bool flag=1; sz[v]=1; for(edge *p=con[v];p;p=p->next) if(p->t!=fa) { getroot(p->t,v,root); sz[v]+=sz[p->t]; if(sz[p->t]*2>n) flag=0; } if(sz[v]*2<n) flag=0; if(flag) root=(root?-1:v); }ll solve(){ int rt=0; getroot(1,-1,rt); if(rt==-1) return jie[n/2]*jie[n/2]%mod; for(edge *p=con[rt];p;p=p->next) sz[p->t]=-(sz[p->t]>sz[rt]?n-sz[rt]:sz[p->t]); for(int i=1;i<=n;i++) sz[i]=(sz[i]>0?0:-sz[i]); int tot=0; g[0][0]=1; for(int i=1;i<=n;i++) { tot+=sz[i]; memset(g[i&1],0,sizeof(g[i&1])); for(int j=0;j<=tot;j++) for(int k=0;k<=min(sz[i],j);k++) g[i&1][j]=(g[i&1][j]+g[(i&1)^1][j-k]*C(sz[i],k)%mod*A(sz[i],k)%mod)%mod; } ll ans=0; for(int i=0,r=1;i<=n;i++,r=-r) ans=(ans+(g[n&1][i]*jie[n-i]%mod*r+mod)%mod)%mod; return ans; }int main(){ scanf("%d",&n); jie[0]=1; for(int i=1;i<=n;i++) jie[i]=jie[i-1]*i%mod; inv[n]=ksm(jie[n],mod-2); for(int i=n-1;i>=0;i--) inv[i]=inv[i+1]*(i+1)%mod; //for(int i=0;i<=n;i++) // cout<<jie[i]<<' '<<inv[i]<<endl; for(int i=1;i<n;i++) { int x,y; scanf("%d%d",&x,&y); ins(x,y); ins(y,x); } printf("%lld",solve()); return 0;}
阅读全文