P3047 [USACO12FEB]Nearby Cows G
阿新 • • 發佈:2022-03-25
很經典很好的一個樹形dp
很明顯dp[i][j]表示距離i在範圍j以內的權值和
我們還很容易想到維護一個deep[i][j]陣列
表示i的子樹中距離i在範圍j以內的權值和
這個題難就難在距離i在範圍j以內的點可能是在i的上頭
這時候轉移方程就要考慮率容斥一下
對於(u,v)這條邊
dp[u][kk]=dp[v][kk-1]-deep[u][kk-2]+deep[u][kk-1];
這個轉移方程真的絕絕子,以後很多題目都可以聯想到這個方法
還有很多細節在下面程式碼裡面,要想完整寫出來確實很有難度
點選檢視程式碼
#include<bits/stdc++.h> using namespace std; #define lowbit(x) x&(-x) #define ll long long const int maxn=1e5+5; int val[maxn]; vector<int>Q[maxn]; int n,k; int d[maxn][25],dp[maxn][25]; void dfs(int u,int fa,int kk){ d[u][kk]+=val[u];//首先要加上他本身 for(int i=0;i<Q[u].size();i++){ int to=Q[u][i]; if(to==fa)continue; dfs(to,u,kk);//由子節點更新而來,所以先遍歷子節點 d[u][kk]+=d[to][kk-1]; } } //dp[v][kk]=dp[fa[v]][kk-1]-d[v][kk-2]+d[v][kk] void calc(int u,int fa,int kk){ //因為由父親轉移過來,先轉移再calc if(kk>=2) dp[u][kk]=dp[fa][kk-1]-d[u][kk-2]+d[u][kk]; else dp[u][kk]=dp[fa][kk-1]+d[u][kk];//在kk<2的情況下,就壓根不需要容斥了,dp[fa][kk-1]就沒有包括d[u][kk-2] for(int i=0;i<Q[u].size();i++){ int to=Q[u][i]; if(to==fa)continue; calc(to,u,kk); } } int main(){ scanf("%d%d",&n,&k); for(int a,b,i=1;i<=n-1;i++){ scanf("%d%d",&a,&b); Q[a].push_back(b); Q[b].push_back(a); } for(int i=1;i<=n;i++) scanf("%d",&val[i]),dp[i][0]=d[i][0]=val[i]; for(int i=1;i<=k;i++) dfs(1,1,i),dp[1][i]=d[1][i];//設定初始根節點 for(int kk=1;kk<=k;kk++) for(int i=0;i<Q[1].size();i++)//要越過根節點再進行dp,因為根節點沒有父親沒法轉移,並且根節點的狀態我們已經初始化了 calc(Q[1][i],1,kk); for(int i=1;i<=n;i++)printf("%d\n",dp[i][k]); return 0; }