1. 程式人生 > 其它 >P3047 [USACO12FEB]Nearby Cows G

P3047 [USACO12FEB]Nearby Cows G

很經典很好的一個樹形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;
}