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