首页 > 其他分享 >【JLOI2016_SHOI2016】侦察守卫(树形DP)

【JLOI2016_SHOI2016】侦察守卫(树形DP)

时间:2022-10-28 20:57:21浏览次数:59  
标签:JLOI2016 ch 子树内 距离 子树 SHOI2016 监听 DP

考虑树形 DP,假设我们已经考虑完当前子树内监听点的放置情况,根为 \(u\),考虑我们要记录什么状态:\(u\) 子树内的监听点向子树外还能监听多远,\(u\) 子树内距离根最远的未被监听点有多远。

发现当第二个状态存在时,第一个状态是无用的,因为若 \(u\) 子树内存在一个未被监听的点 \(v\),设其到 \(u\) 的距离为 \(d\),那么:

  • \(u\) 子树内的监听点最远向子树外的监听距离不会超过 \(d\),否则 \(v\) 就能被监听。
  • \(v\) 最后肯定被 \(u\) 子树外的某个点 \(x\) 监听,那么所有距离 \(u\) 小于等于 \(d\) 的点都能被 \(x\) 监听到。

也就是说当 \(v\) 存在时,我们无需考虑 \(u\) 子树内的监听点向 \(u\) 子树外的监听情况,第一个状态是无用的。

那么这样就能简化 DP 状态:我们设 \(f_{u,i}\) 表示考虑完 \(u\) 子树内监听点的放置状况,\(u\) 子树内离 \(u\) 最远的未被监听的点的距离为 \(i\),所需的最小代价。设 \(g_{u,i}\) 表示考虑完 \(u\) 子树内监听点的放置状况,\(u\) 子树内已经不存在未被监听的点,且 \(u\) 子树内的监听点向子树外最远能监听距离 \(i\),所需的最小代价。

转移时合并 \(u\) 当前已经考虑的子树和枚举的儿子 \(v\) 的子树,分 \(f_u,f_v\)、\(f_u,g_v\)、\(g_u,f_v\)、\(g_u,g_v\) 四种情况合并:

\[\begin{aligned} f_{u,i}+f_{v,j}&\to f_{u,\max(i,j+1)}\\ f_{u,i}+g_{v,j}&\to \begin{cases}g_{u,j-1}&\text{if }j-1\geq i\\f_{u,i}&\text{if }j-1<i\end{cases}\\ g_{u,i}+f_{v,j}&\to \begin{cases}g_{u,i}&\text{if }i\geq j+1\\f_{u,j+1}&\text{if }i<j+1\end{cases}\\ g_{u,i}+g_{v,j}&\to g_{u,\max(i,j-1)} \end{aligned} \]

使用前缀和/后缀和优化即可做到 \(O(nD)\)。

初始时每个点都各自形成单独一棵子树,对于所有点设 \(g_{u,D}\gets w_u\),对需要被监听的点设 \(f_{u,0}=0\),对不需要被监听的点设 \(g_{u,0}=0\)。

#include<bits/stdc++.h>

#define D 25
#define N 500010
#define INF 0x7fffffff

using namespace std;

inline void upmin(int &x,int y){if(y<x) x=y;}

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<<1)+(x<<3)+(ch^'0');
		ch=getchar();
	}
	return x*f;
}

int n,d,w[N];
int cnt,head[N],nxt[N<<1],to[N<<1];
int f[N][D],g[N][D],pref[N][D],preg[N][D];

void adde(int u,int v)
{
	to[++cnt]=v;
	nxt[cnt]=head[u];
	head[u]=cnt;
}

inline void premin(int *f,int *pre)
{
	for(int i=0;i<=d;i++)
		pre[i]=min(i?pre[i-1]:INF,f[i]);
}

void dfs(int u,int fa)
{
	static int ff[D],gg[D];
	premin(f[u],pref[u]),premin(g[u],preg[u]);
	for(int i=head[u];i;i=nxt[i])
	{
		int v=to[i];
		if(v==fa) continue;
		dfs(v,u);
		memset(ff,0x3f,sizeof(ff));
		memset(gg,0x3f,sizeof(gg));
		for(int i=1;i<=d;i++)
			upmin(ff[i],f[u][i]+pref[v][i-1]);
		for(int i=0;i<d;i++)
			upmin(ff[i+1],pref[u][i+1]+f[v][i]);
		for(int i=1;i<=d;i++)
			upmin(gg[i-1],pref[u][i-1]+g[v][i]);
		for(int i=0;i<=d;i++)
			upmin(ff[i],f[u][i]+preg[v][i]);
		for(int i=1;i<=d;i++)
			upmin(gg[i],g[u][i]+pref[v][i-1]);
		for(int i=0;i<d;i++)
			upmin(ff[i+1],preg[u][i]+f[v][i]);
		for(int i=0;i<=d;i++)
			upmin(gg[i],g[u][i]+preg[v][min(i+1,d)]);
		for(int i=1;i<=d;i++)
			upmin(gg[i-1],preg[u][i-1]+g[v][i]);
		memcpy(f[u],ff,sizeof(f[u]));
		memcpy(g[u],gg,sizeof(g[u]));
		premin(f[u],pref[u]),premin(g[u],preg[u]);
	}
}

int main()
{
	n=read(),d=read();
	memset(f,0x3f,sizeof(f));
	memset(g,0x3f,sizeof(g));
	for(int i=1;i<=n;i++) g[i][d]=read();
	for(int i=1,m=read();i<=m;i++) f[read()][0]=0;
	for(int i=1;i<=n;i++) if(f[i][0]) g[i][0]=0;
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read();
		adde(u,v),adde(v,u);
	}
	dfs(1,0);
	int ans=INF;
	for(int i=0;i<=d;i++) ans=min(ans,g[1][i]);
	printf("%d\n",ans);
	return 0;
}

标签:JLOI2016,ch,子树内,距离,子树,SHOI2016,监听,DP
From: https://www.cnblogs.com/ez-lcw/p/16837453.html

相关文章

  • 【HNOI2015】实验比较(树形DP,容斥)
    题意:给你一棵树,你要对所有节点定一个顺序序列,形如\(p_1\oplus_1p_2\oplus_2p_3\cdotsp_{n-1}\oplus_{n-1}p_n\),其中\(\oplus_i\)为\(=\)或\(<\),\(p_{1\simn}......
  • 【CF553E】Kyoya and Train(期望dp,SPFA,FFT)
    考虑dp。发现正着dp好像不太好做,毕竟初值不太好设,而且时间一大于\(t\)费用就要加上\(x\),所以考虑倒着dp。设\(f_{u,j}\)表示现在已经到达\(u\)点,耗时\(j\),问......
  • 【BZOJ4987】Tree(树形dp)
    题意:给你一棵\(n\)个节点的树找出\(k\)个不同的点\(a_1,a_2,\cdots,a_k\),使得\(\sum\limits_{i=1}^{k-1}dis(a_i,a_{i+1})\)最小。首先有个容易想到的性质:这\(k......
  • 【BJWC2018】上学路线(dp,Lucas,crt)
    考虑dp。我们先把\((n,m)\)也当做障碍点,然后把所有的障碍点按\(x\)坐标为第一关键字,\(y\)坐标为第二关键字排序。然后设\(f_i\)表示到达第\(i\)个障碍点的合法......
  • 【ARC074E】RGB Sequence(神奇的dp)
    注意到颜色的种类数只有\(3\)种,\(n\leq100\)也很小。然后就有了一种神奇的dp状态:考虑从前往后填方块,设\(dp(i,j,k)\)表示离当前位置最近的颜色位置在\(i\),离当......
  • 【ARC068F】Solitaire(dp,计数,思维)
    首先发现那个双端队列一定长这样:也就是说,这个队列中的数先单调递减,然后再单调递增,最小值为\(1\)。现在考虑从双端队列中取数,那么当我们取到\(1\)这个数时,我们会在原......
  • 【AGC013D】Piling Up(神奇的dp)
    考场上用了一种奇怪的做法,不知道为什么就对了,考完后仔细想才想明白。很巧妙的一种dp方式。首先发现每次操作是拿一个球、放两个球、再拿一个球,总球数不变,所以有\(\tex......
  • 【AGC005D】_K Perm Counting(容斥,二分图,计数dp)
    首先正面做不太好做,考虑容斥。设\(f(m)\)表示排列中至少有\(m\)处\(|P_i-i|=k\)的方案数。那么答案就是\(\sum\limits_{i=0}^n(-1)^if(i)\)。原题可以看成一个二......
  • 【AGC003F】Fraction of Fractal(dp,矩阵快速幂)
    先说一下下文会用到的定义或称呼的意思:称单位分形为题目给出的\(1\)级分形。称一种分形左右联通,则说明将两个这种分形左右放在一起时,至少有一个连通块是跨越这两个......
  • 【51Nod1386】双马尾机器人(分块+dp)
    对于这种找互质的数的集合的题,一般是讨论每个数的质因子会不会有重复。听说这种互质的题把质因子分为小于\(\sqrt{n}\)和大于\(\sqrt{n}\)是经典套路?因为当\(n\)很......