树形DP【洛谷P3047】 [USACO12FEB]附近的牛Nearby Cows

P3047 [USACO12FEB]附近的牛Nearby Cows

农民约翰已经注意到他的奶牛经常在附近的田野之间移动。考虑到这一点,他想在每一块土地上种上足够的草,不仅是为了最初在这片土地上的奶牛,而且是为了从附近的田地里去吃草的奶牛。

具体来说,FJ的农场由N块田野构成(1 <= n <= 100,000),每两块田野之间有一条无向边连接(总共n-1条边)。FJ设计了农场,任何两个田野i和j之间,有且只有一条路径连接i和j。第 i块田野是C(i)头牛的住所,尽管奶牛们有时会通过k条路到达其他不同的田野(1<=k<=20)。

FJ想在每块田野上种上够M(i)头奶牛吃的草。M(i)指能从其他点经过最多k步就能到达这个点的奶牛的个数。

现给出FJ的每一个田野的奶牛的数目,请帮助FJ计算每一块田野的M(i)。

先处理出来子树内的。

\(f(i)(j)\)表示以i为根的子树内距离i为j的答案。

很简单的转移:

\(f(u)(j)=\sum_{f(v)(j-1)}​\)

然后转移出非子树内的,为了解决DP后效性要开一个中间量数组转移一下。

code:

#include <iostream>
#include <cstdio>

#define int long long

using namespace std;

const int wx=100017;

inline int read(){
	int sum=0,f=1; char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
	while(ch>='0'&&ch<='9'){sum=(sum<<1)+(sum<<3)+ch-'0'; ch=getchar();}
	return sum*f;
}

int f[wx][27],g[wx][27];
int head[wx],val[wx];
int n,k,num;

struct e{
	int nxt,to;
}edge[wx*2];

void add(int from,int to){
	edge[++num].nxt=head[from];
	edge[num].to=to;
	head[from]=num;
}

void dfs(int u,int fa){
	f[u][0]=val[u];
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(v==fa)continue;
		dfs(v,u);
		for(int j=1;j<=k;j++){
			f[u][j]=(f[u][j]+f[v][j-1]);
		}
	}
}

void dp(int u,int fa){
	for(int i=head[u];i;i=edge[i].nxt){
		int v=edge[i].to;
		if(v==fa)continue;
		g[v][1]+=f[u][0];
		for(int j=2;j<=k;j++){
			g[v][j]+=(f[u][j-1]-f[v][j-2]);
		}
		for(int j=1;j<=k;j++)f[v][j]+=g[v][j];
		dp(v,u);
	}
}

signed main(){
//	freopen("a.in","r",stdin);
//	freopen("a.out","w",stdout);
	n=read(); k=read();
	for(int i=1;i<n;i++){
		int x,y;
		x=read(); y=read();
		add(x,y); add(y,x);
	}
	for(int i=1;i<=n;i++)val[i]=read();
	dfs(1,0); dp(1,0);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=k;j++){
			f[i][j]+=f[i][j-1];
		}
	}
	for(int i=1;i<=n;i++)printf("%lld\n",f[i][k]);
	
	return 0;
}
posted @ 2018-10-30 16:10  _王小呆  阅读(150)  评论(0编辑  收藏  举报