AT3728 Squirrel Migration

AT3728 Squirrel Migration 

就是给每个点分配两个匹配点(自环除外)

考虑最大值

考虑极限情况:每个边的贡献是min(sz[u],sz[v])*2

证明存在方案:

发现,如果哪边sz更小,就把这些边都往外连

这样,在重心的位置,会两两匹配闭合。

所以存在构造方案。

 

方案数?就是最后匹配的方案

重心两个:((n/2)!)^2

重心一个:

也就是,以重心为根,每个子树是一个组,每个组必须匹配别的组

而重心自己可以连自环或者匹配任意一个组

枚举重心连自环与否,做两遍。

 

现在有若干个组,每个组有num[i]个元素,每个组不能匹配自己的元素

考虑容斥!

f[i][j]前i个组,有j个位置连了自己组的元素的方案数

f[i][j+k]+=f[i-1][j]*C(num[i],k)*A(num[i],k)

看似O(n^3),实际和树形背包的sz优化一样,就是O(n^2)的

 

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define fi first
#define se second
#define mk(a,b) make_pair(a,b)
#define numb (ch^'0')
#define pb push_back
#define solid const auto &
#define enter cout<<endl
#define pii pair<int,int>
using namespace std;
typedef long long ll;
template<class T>il void rd(T &x){
    char ch;x=0;bool fl=false;while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);(fl==true)&&(x=-x);}
template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');}
template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');}
template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');}
namespace Modulo{
const int mod=1e9+7;
int ad(int x,int y){return (x+y)>=mod?x+y-mod:x+y;}
void inc(int &x,int y){x=ad(x,y);}
int mul(int x,int y){return (ll)x*y%mod;}
void inc2(int &x,int y){x=mul(x,y);}
int qm(int x,int y=mod-2){int ret=1;while(y){if(y&1) ret=mul(x,ret);x=mul(x,x);y>>=1;}return ret;}
}
using namespace Modulo;
namespace Miracle{
const int N=5005;
int n;
struct node{
    int nxt,to;
}e[2*N];
int hd[N],cnt;
int jie[N],inv[N];
int C(int n,int m){
    if(n<0||m<0||n<m) return 0;
    return mul(jie[n],mul(inv[m],inv[n-m]));
}
int A(int n,int m){
    return mul(C(n,m),jie[m]);
}
void add(int x,int y){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    hd[x]=cnt;
}
int be[N],sz[N],num[N],tot;
int rt[N];
void dfs(int x,int fa){
    sz[x]=1;
    int mx=0;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa) continue;
        dfs(y,x);
        sz[x]+=sz[y];
        if(sz[y]>mx) mx=sz[y];
    }
    mx=max(mx,n-sz[x]);
    if(mx<=n/2){
        rt[++rt[0]]=x;
    }
}
void fin(int x,int fa){
    be[x]=tot;
    ++num[be[x]];
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa) continue;
        if(x==rt[1]){
            ++tot;
        }
        fin(y,x);
    }
}
int f[N][N];
int calc(){
    memset(f,0,sizeof f);
    f[0][0]=1;
    int sum=0;
    for(reg i=1;i<=tot;++i){
        // cout<<" i "<<i<<" "<<num[i]<<endl;
        for(reg j=0;j<=sum;++j){
            for(reg k=0;k<=num[i];++k){
                // cout<<" con "<<mul(f[i-1][j],mul(C(num[i],k),A(num[i],k)))<<endl;
                f[i][j+k]=ad(f[i][j+k],mul(f[i-1][j],mul(C(num[i],k),A(num[i],k))));
            }
        }
        sum+=num[i];
    }
    ll ret=0;
    for(reg j=0;j<=sum;++j){
        // cout<<f[tot][j]<<endl;
        if(j&1){
            ret=ad(ret,mod-mul(f[tot][j],jie[sum-j]));
        }else{
            ret=ad(ret,mul(f[tot][j],jie[sum-j]));
        }
    }
    return ret;
}
int main(){
    rd(n);
    jie[0]=1;
    for(reg i=1;i<=n;++i) jie[i]=mul(jie[i-1],i);
    inv[n]=qm(jie[n]);
    for(reg i=n-1;i>=0;--i) inv[i]=mul(inv[i+1],i+1);
    int x,y;
    for(reg i=1;i<n;++i){
        rd(x);rd(y);
        add(x,y);add(y,x);
    }
    dfs(1,0);
    if(rt[0]==2){
        ll ans=qm(jie[n/2],2);
        ot(ans);return 0;
    }
    // cout<<rt[1]<<endl;
    fin(rt[1],0);
    // cout<<" tot "<<tot<<endl;
    // prt(be,1,n);
    int ans=calc();
    // cout<<" ans1 "<<ans<<endl;
    num[++tot]=1;
    ans=ad(ans,calc());
    ot(ans);
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
*/

构造最大值,考虑上界,再证明能不能达到。

然后构造方案很明确了,方案数就是分组,容斥DP来处理

posted @ 2019-06-04 11:10  *Miracle*  阅读(206)  评论(0编辑  收藏  举报