-
$P3047 [USACO12FEB]$附近的牛$Nearby Cows$
#$Description$
[题面](https://www.luogu.org/problem/P3047)
题意是给你一颗$n$个节点的树和$k(kleq 20)$,每条边权是$1$,告诉你$n$个点的点权,输出到点$i$距离$leq k$的所有点的点权和
#$Solution$
感觉很不可做,一开始考虑维护距离为$k$的点,然后跟着当前点往一个方向走,发现根本没法实现。
考虑到一维状态不够用,肯定要使用二维$dp$,第一维已经达到了$1e5$的数据量,盲猜第二维是$leq k$的数。
于是$dp[i][j]$表示到$i$点距离$leq j$的点权和,考虑一个子节点是$s$,如何进行转移。
若要求子节点的所有贡献都能转移到$dp[i][j]$,一定要转移来的是距离不超过$j-1$的,因为从$i-->s$需要走一步,所以剩下走到最远只能走$j-1$步
所以$dp[i][j]+=dp[s][j-1](sin son[i])$
这样做向下找保证两个边界恰好相同,但向上找就会发现$dp[s][j-1]$是$s$向上走$j-1$步,也就是走$1$步到$i$,再从$i$往其他方向走$j-2$步
看这张图
![](https://img2018.cnblogs.com/blog/1564177/201911/1564177-20191103162624013-46334772.png)
红色部分是加入的$dp[s][j-1]$,我们需要消除红色部分超越父亲节点到其他节点的影响,所以每次$+dp[s][j-1]-dp[i][j-2]$即可
但最后注意到中间一部分(也就是$i$向外扩展$j-2$)每次都被减去,最后没有加入中间部分答案,所以只需要再加一次$dp[i][j-2]$即可
有点类似容斥的思想
具体实现因为每次都要减去$dp[i][j-2]$,所以统计完所有子树后$-dp[i][j-2]*(son[i]-1)$即可
但注意到一个问题,如果使用常规的$dfs$序父亲无法统计,于是根本不用$dfs$,直接$for$循环所有节点和所有距离统计答案就行,但要注意枚举顺序的问题,假如外层先枚举节点,那么$i=1,j=3$时其他节点的$j=2$的情况都没被更新,这样$dp$肯定错了,所以换一换枚举顺序,外层先枚举$j$也就是距离即可
#$Code$
```
#include
#include
#include
#include
#define re register
#define maxn 100100
using namespace std;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
struct Edge{
int v,nxt;
}e[maxn<<2];
int n,k,x,y,c[maxn],head[maxn],cnt;
int dp[maxn][25],du[maxn];
inline void add(int u,int v)
{
e[++cnt].v=v;
e[cnt].nxt=head[u];
head[u]=cnt;
du[u]++;
}
void DP()
{
for(re int j=1;j<=k;++j)
for(re int i=1;i<=n;++i)
{
for(re int p=head[i];p;p=e[p].nxt)
dp[i][j]+=dp[e[p].v][j-1];
if(j==1) dp[i][j]+=dp[i][0];
else dp[i][j]-=dp[i][j-2]*(du[i]-1);
}
}
int main()
{
n=read(),k=read();
for(re int i=1;i
-
相关阅读:
elasticsearch 基本操作
ElasticSearch停止启动
oracle误删数据
多层级sql查询
max_result_window
测试ik分词效果
TransportClient 新建index,mappings dynamic_templates。
7.10考试
C#生成TXT文件
C#的进度条--progressBar
-
原文地址:https://www.cnblogs.com/Liuz8848/p/11787629.html
Copyright © 2020-2023
润新知