一道比較基礎的計數題,仍是一個經常使用的單獨計算貢獻的例子。git
首先看題目和範圍,暴力枚舉確定是不可行的,並且\(O(n\ logn)\)的算法貌似很難寫。算法
那咱們就來想\(O(n)\)的吧,咱們單獨考慮每一條邊的貢獻,咱們注意到一個重要的性質:ui
樹上任意兩點間的最短路徑都是惟一肯定的。spa
這個常識吧,因此咱們只須要考慮每一條邊兩邊的點在計算時會通過這條邊多少次。code
咱們枚舉每一條邊,而後能夠這樣考慮這一條邊:blog
咱們設一邊有\(x\)個點,另外一邊有\(y\)個點,很明顯\(x+y=n\)string
而後咱們考慮有多少點之間的路徑會通過這條邊it
用上面的那個性質能夠發現,只要在這條邊的兩邊都有點時就知足條件。io
而後咱們容斥一下就知道答案爲:\(C_n^k-C_x^k-C_y^k\)class
再注意一下在本題中咱們規定當\(a>b\)時\(C_b^a=0\)
關於那個每一條邊兩邊的點數,咱們DFS預處理一遍後獲得一邊的點數,而後根據上面講的減一下得出另外一邊的點數便可。
CODE
#include<cstdio> #include<cstring> #include<cctype> const int N=100005,mod=1e9+7; struct edge { int to,next; }e[N<<1]; int head[N],fac[N],n,x,y,ans,k,cnt,rt=1,tot,size[N],inv[N]; inline char tc(void) { static char fl[100000],*A=fl,*B=fl; return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++; } inline void read(int &x) { x=0; char ch; while (!isdigit(ch=tc())); while (x=(x<<3)+(x<<1)+ch-'0',isdigit(ch=tc())); } inline void double_add(int x,int y) { e[++cnt].to=y; e[cnt].next=head[x]; head[x]=cnt; e[++cnt].to=x; e[cnt].next=head[y]; head[y]=cnt; } inline int quick_pow(int x,int p) { int tot=1; while (p) { if (p&1) tot=1LL*tot*x%mod; x=1LL*x*x%mod; p>>=1; } return tot; } inline int C(int n,int k) { if (n<k) return 0; if (n==k) return 1; return 1LL*fac[n]*inv[k]%mod*inv[n-k]%mod; } inline void DFS(int now,int fa) { register int i; size[now]=1; for (i=head[now];~i;i=e[i].next) if (e[i].to!=fa) DFS(e[i].to,now),size[now]+=size[e[i].to]; } int main() { //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout); register int i; read(n); read(k); memset(head,-1,sizeof(head)); for (fac[1]=inv[1]=1,i=2;i<=n;++i) fac[i]=1LL*fac[i-1]*i%mod,inv[i]=quick_pow(fac[i],mod-2); for (i=1;i<n;++i) read(x),read(y),double_add(x,y); DFS(rt,-1); tot=C(n,k); for (i=1;i<=n;++i) ans=((1LL*ans+tot-C(size[i],k)+mod)%mod-C(n-size[i],k)+mod)%mod; return printf("%d",ans),0; }