3078 倉庫

Task
給n個節點中有K個入口的樹,求有多少個1~n的排列順序,順序遍歷每個節點時,都存在都入口到該節點的路徑,滿足路徑上不包含之前遍歷過的點。答案mod 1e9+7。
這裏寫圖片描述
Solution
1. K=1。
在樹上求方案數,容易想到是樹形dp。
以入口S作爲根,發現遍歷節點x之前,x的子樹必須都已經被遍歷過了,dp[ i ]表示,以i爲根的子樹都已經被遍歷的方案數。X子樹對應的序列爲兒子們t1,t2~tn的序列的組合。保持各小序列相對位置不變,穿插組合成一個大序列,再在結尾加上x的序列。用排列組合(乘法逆元和快速冪)來實現。

  1. K=2序列上的問題
    a) 是一條鏈,且S1,S2在鏈的兩端。
    定義dp[ l ][ r ]爲[ l , r ]這個區間都被遍歷的方案數,如果端點L,R都被遍歷了,那麼L,R裏面的點也被遍歷了。
    最後一個點取的只可能是l或r.
    dp[ l ][ r ]=dp[ l+1 ][ r ]+dp[ l ][ r-1 ]
    特別的,當l=r時dp[ l ][ r ]=1
    b) 是一棵樹。
    以S1,S2爲兩端,把樹拉成一條鏈,鏈上的每一個點代表一棵樹。
    對於鏈上的每一個點,先用K=1的樹形dp預處理出這棵樹的方案數,dp[i].
    最後一個取的只可能是L上的根或者R上的根,記爲rt,但是rt下的節點,卻可以與之前的序列任意組合。
    DP[ l ][ R ]=C( sum( L, R )-1,sum( L,L )-1 )*DP[ L+1 ][ R ]*dp[ id [ L ] ]+
    C ( sum(L,R )- 1,sum( R,R )- 1 )*DP[ L+1 ][ R ] *dp[ id [R] ]
    這裏寫圖片描述
const int M=1e5+5,N=2e3+3,P=1e9+7;
int n,m,ecnt,S1,S2;
int head[M],f[M];
struct edge{
    int t,nxt;
}e[M<<1];
inline void addedge(int f,int t){
    e[++ecnt]=(edge){t,head[f]};
    head[f]=ecnt;
}
inline void input(){
    int i,j,k,a,b;
    rd(n);rd(m);
    rd(S1);
    if(m==2)rd(S2);
    rep(i,1,n-1){
        rd(a);rd(b);
        addedge(a,b);
        addedge(b,a);
    }
}
inline void init(){
    int i;
    f[0]=1;
    rep(i,1,n)f[i]=1ll*f[i-1]*i%P;
}
inline int fst_pow(int a,int p){
    int ans=1;
    while(p){
        if(p&1)ans=1ll*ans*a%P;
        a=1ll*a*a%P;
        p>>=1;
    }
    return ans;
}
inline int C(int a,int b){return 1ll*f[a]*fst_pow(1ll*f[b]*f[a-b]%P,P-2)%P;}
struct P40{//樹形dp[i],i子樹內被填滿 排序的方案數 
    int dp[M],sz[M];
    inline void dfs(int f,int x){
        dp[x]=1;
        for(int i=head[x];i;i=e[i].nxt){
            if(e[i].t==f)continue;
            dfs(x,e[i].t);
            sz[x]+=sz[e[i].t];
            dp[x]=1ll*dp[x]*dp[e[i].t]%P*C(sz[x],sz[e[i].t])%P; 
        }
        sz[x]++;
    }
    inline void solve(){
        dfs(0,S1);
        sc(dp[S1]);
    }
}P40;
struct P60{
    int dp[N],DP[N][N],sum[N],fa[N],id[N],sz[N];
    int num;
    bool mark[N];
    inline void dfs1(int f,int x){
        fa[x]=f;
        for(int i=head[x];i;i=e[i].nxt)if(e[i].t!=f)dfs1(x,e[i].t);
    }
    inline void getarray(){
        int i,x=S1;
        while(x!=fa[S2]){
            mark[x]=1;//標記序列上的點 
            id[++num]=x;//重新編號 
            x=fa[x];
        }
    }
    inline void dfs2(int f,int x){
        dp[x]=1;
        for(int i=head[x];i;i=e[i].nxt){
            if(e[i].t==f||mark[e[i].t])continue;
            dfs2(x,e[i].t);
            sz[x]+=sz[e[i].t];
            dp[x]=1ll*dp[x]*dp[e[i].t]%P*C(sz[x],sz[e[i].t])%P;
        }
        sz[x]++;
    }
    inline void solve(){
        int i,j,k,l,r;
        dfs1(0,S2);
        getarray();
        rep(i,1,num){
            dfs2(0,id[i]);//序列上的一個點就是一棵樹
            sum[i]=sum[i-1]+sz[id[i]];//前綴和 
            DP[i][i]=dp[id[i]];
        }
        rep(i,2,num)
            rep(l,1,num-i+1){
                r=l+i-1;
                DP[l][r]=1ll*DP[l+1][r]*DP[l][l]%P*C(sum[r]-sum[l-1]-1,sum[l]-sum[l-1]-1)%P+1ll*DP[l][r-1]*DP[r][r]%P*C(sum[r]-sum[l-1]-1,sum[r]-sum[r-1]-1)%P;
                if(DP[l][r]>=P)DP[l][r]-=P;
            }
        sc(DP[1][num]);
    }
}P60;
int main(){
//  freopen("0.in","r",stdin);
    input();
    init();
    if(m==1)P40.solve();
    else P60.solve();
    return 0;
}
發佈了28 篇原創文章 · 獲贊 0 · 訪問量 5408
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章