[ARC087]F

来源:互联网 发布:淘宝win10激活码来源 编辑:程序博客网 时间:2024/05/12 14:58

题面
先考虑每条边(u,v)的松鼠通过量,最大显然为2min(sizeu,sizev),这里的size指去掉这条边后的两个子树大小。所以我们分两种情况。
有两个重心:答案显然为(n2)!2
有一个重心:我们设去掉重心后的子树为T1,T2,...,TrT为它们的并。
要让答案最大,那么不能存在有某只松鼠从Ti中跑回Ti中。
我们考虑容斥原理计算方案数:

Ans=ST(1)|S|fS

其中fS为集合S中的松鼠都跑回本组中的方案数。所以我们只需计算gk=|S|=kfS,也就是有多少组(s1,s2,...,sk),(t1,t2,...,tk),满足s1<s2<..<sksiti在同一组中。这个DP一下就好,设gi,j为考虑前i组,选了j个的方案,那么gi,j+=gi1,jkCksizeiAksizei。所以答案即为:
Ans=i=0n1(1)igi(Ni)!

代码:

#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;}
阅读全文
'); })();
0 0
原创粉丝点击
热门IT博客
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 浏览器排行榜 什么手机浏览器好用 手机浏览器排行榜 有网盘的浏览器 浏览器火狐 下载浏览器大全 什么浏览器 浏览器软件 好的浏览器 手机浏览器哪个好 电脑什么浏览器好用 电脑浏览器哪个好用 什么浏览器好用 种子浏览器 浏览器更新 web浏览器 2345游览器下载 web浏览器是什么 用浏览器 浏览器下载大全 浏览器英文 抖音怎么看历史浏览 长沙浏阳 浏阳 浏阳市 浏阳河歌词 浏阳烟花 浏阳河简谱 浏阳火车站 浏阳人才网 又唱浏阳河 浏阳论坛 浏阳河歌曲 浏阳烟花节 浏阳河原唱 浏阳机场 湖南浏阳 浏阳蓝思 浏阳招聘网 浏阳人才 浏阳论坛网