1. 程式人生 > >[CF735E/736C]Ostap and Tree

[CF735E/736C]Ostap and Tree

樹形 AR getc 邊距 bsp 復雜度 選擇 長度 for

題目大意:
  一個$n(n\le100)$個點的樹,將一些點染成黑點,求滿足每個點到最近黑點的距離$\le k(k\le\min(20,n-1))$的方案數。

思路:
  樹形DP。
  用$f[i][j]$表示$i$的子樹中離$i$最近黑點的距離為$j$,且距離超過$j$的點都被滿足的方案數。轉移時新建一個臨時數組$tmp$保存轉移後的$f[x]$。設$y$是$x$的子結點,枚舉$f[x][i]$和$f[y][j]$,轉移如下:
    1.若$i+j\le2k$,則此時$\min(i,j+1)\le k$,對於長度為$i+j+1$的鏈上的所有點都可以找到一邊距離$\le k$,因此狀態合並以後是合法狀態,轉移$tmp[\min(i,j+1)]+=f[x][i]\times f[y][j]$;

    2.若$i+j>2k$,則此時$\max(i,j+1)>k$,鏈上肯定會存在一些點兩邊都夠不到,轉移$tmp[\max(i,j+1)]+=f[x][i]\times f[y][j]$。
  初始狀態$f[x][0]=1$,表示不考慮子樹內的情況,選擇自己的方案數為$1$;$f[x][k+1]=1$,表示自己本身不滿足,但子結點都被滿足的情況,主要是方便轉移。
  答案為$\sum_{i<=k}f[root][i]$。
  時間復雜度$O(nk^2)$。

 1 #include<cstdio>
 2 #include<cctype>
 3
#include<algorithm> 4 #include<forward_list> 5 typedef long long int64; 6 inline int getint() { 7 register char ch; 8 while(!isdigit(ch=getchar())); 9 register int x=ch^0; 10 while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^0); 11 return x;
12 } 13 const int N=101,K=41,mod=1e9+7; 14 int k,f[N][K],tmp[K]; 15 std::forward_list<int> e[N]; 16 inline void add_edge(const int &u,const int &v) { 17 e[u].push_front(v); 18 e[v].push_front(u); 19 } 20 void dfs(const int &x,const int &par) { 21 f[x][0]=f[x][k+1]=1; 22 for(int &y:e[x]) { 23 if(y==par) continue; 24 dfs(y,x); 25 std::fill(&tmp[0],&tmp[k*2]+1,0); 26 for(register int i=0;i<=k*2;i++) { 27 for(register int j=0;j<=k*2;j++) { 28 (tmp[i+j<=k*2?std::min(i,j+1):std::max(i,j+1)]+=(int64)f[x][i]*f[y][j]%mod)%=mod; 29 } 30 } 31 std::copy(&tmp[0],&tmp[k*2]+1,f[x]); 32 } 33 } 34 int main() { 35 const int n=getint();k=getint(); 36 for(register int i=1;i<n;i++) { 37 add_edge(getint(),getint()); 38 } 39 dfs(1,0); 40 int ans=0; 41 for(register int i=0;i<=k;i++) { 42 (ans+=f[1][i])%=mod; 43 } 44 printf("%d\n",ans); 45 return 0; 46 }

[CF735E/736C]Ostap and Tree