洛谷P3267 侦察守卫

侦察守卫

时间复杂度:O(nd)

以前整理的比较笼统

首先,我做的时候设的是 f 表示向下 j 层, g 是上下一起

转移 fu,j=fv,j1

但这显然不对

这是因为我没考虑清楚一些东西:

  1. 关键点与普通点的区别

  2. 覆盖会有一大部分是重叠的,别的点帮忙没考虑

先看第二个点

对于别的点帮忙,只需要考虑儿子对父亲的

为啥?

因为儿子对儿子的和父亲对儿子的很好处理,只需要在转移中不累加而是取 min

那还有对祖先的呢,这样就不能单纯 -1 了吧

对,所以加一维 j 表示向上覆盖几层

对于儿子给父亲覆盖完了的点,父亲就不用覆盖了,这需要在状态里扣掉

于是设 gu,j 表示 g 向下 j1 层有没有被覆盖随便,j 层及以下全部被覆盖的最小花费

关于第一点,我们可以不覆盖普通点

转化一下,如果这个点覆盖的点全是普通点,那么这个点覆不覆盖也没啥影响,这题算的是花费,我们直接把他的花费刨除掉,就是 fu,0=gu,0=0

但是,他可以转移成 0,但如果要用它去覆盖其他点,他的花费是要算的,于是 fu,j=valu ,jd

想转移时要把所有情况都列出来

  1. 我覆盖我,我的儿子被我覆盖

  2. 我被我的儿子覆盖

万事俱备!江东纵火团!放转移!

fu,i=min(fu,i+gv,i,gu,i+1+fv,i+1)

前面的好理解,第二个就是 u 要向上覆盖 i 层,v 就要向上覆盖 i+1 层,并且 v 向上覆盖一层后再折下来,可以向下覆盖 i 层,所以 ug 要到 i+1

注意这里其实 g 都是要取一个 ,但转移时随着 fu,i 的更新就相当于取了

所以,转移是要先更新 f 再更新 g

代码很史

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=5e5+5;
const int inf=9e18;
int n,d,val[N],head[N<<1],cnt;
bool vis[N];
struct node{int to,nxt;}e[N<<1];
void add(int u,int v){
	e[++cnt].nxt=head[u];
	head[u]=cnt;
	e[cnt].to=v;
}
int f[N][22],g[N][22];
void dfs(int u,int fa){
	if(vis[u])f[u][0]=g[u][0]=val[u];
	else f[u][0]=g[u][0]=0;
	for(int i=1;i<=d;i++)f[u][i]=val[u];
	f[u][d+1]=inf;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa)continue;
		dfs(v,u);
		for(int j=d;j>=0;j--)f[u][j]=min(f[u][j]+g[v][j],f[v][j+1]+g[u][j+1]);
		for(int j=d;j>=0;j--)f[u][j]=min(f[u][j],f[u][j+1]);
		g[u][0]=f[u][0];
		for(int j=1;j<=d;j++)g[u][j]+=g[v][j-1];
		for(int j=1;j<=d;j++)g[u][j]=min(g[u][j],g[u][j-1]);
	}
}
signed main(){
	cin>>n>>d;
	for(int i=1;i<=n;i++){
		cin>>val[i];
	}
	int m;
	cin>>m;
	for(int i=1;i<=m;i++){
		int x;
		cin>>x;
		vis[x]=1;
	}
	for(int i=1;i<n;i++){
		int u,v;
		cin>>u>>v;
		add(u,v),add(v,u);
	}
	dfs(1,0);
	cout<<f[1][0];
	return 0;
}
posted @   小惰惰  阅读(4)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
/* 鼠标点击求赞文字特效 */
点击右上角即可分享
微信分享提示