#(树形DP+容斥原理)洛谷P3047 [USACO12FEB]附近的牛Nearby Cows

题目描述

Farmer John has noticed that his cows often move between nearby fields. Taking this into account, he wants to plant enough grass in each of his fields not only for the cows situated initially in that field, but also for cows visiting from nearby fields.

Specifically, FJ's farm consists of N fields (1 <= N <= 100,000), where some pairs of fields are connected with bi-directional trails (N-1 of them in total). FJ has designed the farm so that between any two fields i and j, there is a unique path made up of trails connecting between i and j. Field i is home to C(i) cows, although cows sometimes move to a different field by crossing up to K trails (1 <= K <= 20).

FJ wants to plant enough grass in each field i to feed the maximum number of cows, M(i), that could possibly end up in that field -- that is, the number of cows that can potentially reach field i by following at most K trails. Given the structure of FJ's farm and the value of C(i) for each field i, please help FJ compute M(i) for every field i.

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

具体来说,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)。

输入格式

* Line 1: Two space-separated integers, N and K.

* Lines 2..N: Each line contains two space-separated integers, i and j (1 <= i,j <= N) indicating that fields i and j are directly connected by a trail.

* Lines N+1..2N: Line N+i contains the integer C(i). (0 <= C(i) <= 1000)

第一行:n和k;

后面n-1行:i和j(两块田野);

之后n行:1..n每一块的C(i);

输出格式

* Lines 1..N: Line i should contain the value of M(i).

n行:每行M(i);//i:1..2

输入输出样例

输入 #1
6 2 
5 1 
3 6 
2 4 
2 1 
3 2 
1 
2 
3 
4 
5 
6 
输出 #1
15 
21 
16 
10 
8 
11 

说明/提示

There are 6 fields, with trails connecting (5,1), (3,6), (2,4), (2,1), and (3,2). Field i has C(i) = i cows.

Field 1 has M(1) = 15 cows within a distance of 2 trails, etc.

题目简述:给出一棵n个点的树,每个点上有C_i头牛,问每个点k步范围内各有多少头牛。

感谢@Slager_Z 提供翻译

这道题好像可以暴力???(*╹▽╹*)

正解:树形动规(〃'▽'〃)

分析:

题目简述:给出一颗有点权的树,问所有节点k步范围内的点权和。

状态设计:设f[i][j]表示以i为目标节点,向外扩展k步所能到达的所有节点和!

重点是状态转移方程的考虑。由于总共有n-1条无向边,很明显构成树形结构。那么考虑节点x,和他的所有相连的节点就可以了~

考虑f[x][k]表示以x为目标节点,出发k步内所能到的点权和,那么x的状态肯定是从他的相邻节点转移过来的

考虑x的连接点x1,x2,x3....xn;

则x在x1方向上扩展k步的求解,可以根据x1向外扩展k-1步的状态转移过来!

但是也存在一个问题,那就是f[x1][k-1]只有在x到x1的方向上才有贡献,扩展出经过x1的所有距离不大于k的点权和;

而另一边,也就是从x1->x的方向上,x1走到x的位置后,仍然有k-2的距离可以继续扩散!

也就是说,f[x1][k-1]重复计算了f[x][k-2]除去x---x1方向上(也就是以x1为根节点的子树)的所有值!

那么,如此这样,x点的状态将由它所有的联通节点转移过来,每个儿子所在的方向(子树)都有一次k-2步的计算缺失,

则最后会多出来Son[x]-1次f[x][k-2]的运算溢出,每次都减去就可以得到正解了!

其实这里用到了容斥原理QAQ~

简单的说,存在A,B,C,三个集合

那么A∪B∪C=A+B+C-A∩B-A∩C-B∩C+A∩B∩C;

(手动画个Venn图就明白啦(*^▽^*))

状态转移方程为:k>1时      f[x][k]=Σf[xi][k-1]-(Son[x]-1)*f[x][k-2];

        k=1时:f[x][k]=Σf[xi][k-1]+f[x][0];(此时k-2<0,不存在f[x][k-2]的溢出!,而若k=2,存在f[x][0]的重复计算)

#include<iostream>
#include<cstdio>
using namespace std;
int n,k,f[100001][21];//
int a,b,sonnum[100001];//各个元素的意义在上面
struct edge{//
int next; //用邻接表存储树
int to;
};
edge e[200000];//
int head[200001],num;//
void addedge(int from,int to)//
{
e[++num].next=head[from];//
e[num].to=to;//邻接表
head[from]=num;//
}
int main()
{
scanf("%d %d",&n,&k);//
for(int i=1;i<=n-1;i++)//
{
scanf("%d %d",&a,&b);//
addedge(a,b);//无向图,两个方向都存一次
addedge(b,a);//
sonnum[b]++;//各个点的联通点数+1
sonnum[a]++;//
}
for(int i=1;i<=n;i++)//初始化走零步,也即是各个节点自身的点权
scanf("%d",&f[i][0]);
//dp
for(int i=1;i<=k;i++)//dp的阶段!以走的步数为阶段进行DP!
for(int j=1;j<=n;j++)//枚举dp每个节点
{
for(int t=head[j];t;t=e[t].next)//枚举节点i的所有联通节点
{
f[j][i]+=f[e[t].to][i-1];//累加联通点的贡献值,存在重复
}
if(i>1)
f[j][i]-=f[j][i-2]*(sonnum[j]-1);//若存在运算溢出,减掉重复的部分
else
f[j][1]+=f[j][0];//如果无溢出,则只需加自身点权
}
for(int i=1;i<=n;i++)//
printf("%d\n",f[i][k]);//输出
return 0;
}

-END-

猜你喜欢

转载自www.cnblogs.com/little-cute-hjr/p/11403267.html