bzoj4557: [JLoi2016]侦察守卫 题意:给出一棵n(<=5e5)个点的树,可以选一些点放置守卫,覆盖与其距离不超过d(<=20)的所有点。每个点放置守卫有一定代价。给出m(<=n)个指定点,求所有指定点被覆盖的最小代价。
题解
我们感性地跑这样一个dp: · dp[i][d]表示以i为根的子树中,指定点被完全覆盖的最小代价。 · dp[i][d-j]表示子树中指定点被完全覆盖,同时能够覆盖i向上j层的最小代价。 · dp[i][d+j]表示i向下j层已经被覆盖后,剩下指定点被完全覆盖的最小代价。 怎么描述得这么乱呢 然后我们随便写一写转移就好啦!
复杂度O(nd)。
代码
#include<cstdio>
#define N 500001
#define inf 1e9
#define D 41
using namespace std;
int n,m,d,p,
sum,mn,f[N],c[N][D];
bool ned[N];
int to[N<<
1],hd[N<<
1],lk[N],cnt;
void add(
int u,
int v)
{to[++cnt]=v,hd[cnt]=lk[u],lk[u]=cnt;}
char ch;
void read(
int &x)
{
x=
0;
while(ch<
'0'|ch>
'9')ch=getchar();
while(ch>=
'0'&&ch<=
'9')
x=x*
10+ch-
'0',ch=getchar();
}
void dfs(
int x)
{
int v,i,j;
for(i=lk[x];i;i=hd[i])
if(f[x]!=to[i])
f[v=to[i]]=x,dfs(v),c[x][
0]+=c[v][d<<
1];
for(j=
1;j<=(d<<
1);j++)
{
c[x][j]=c[x][j-
1],mn=inf,
sum=
0;
for(i=lk[x];i;i=hd[i])
if(to[i]!=f[x])
{
v=to[i];
if(j>d)p=c[v][j-
1];
else
{
p=c[v][(d<<
1)-j];
if(mn>c[v][j-
1]-p)mn=c[v][j-
1]-p;
}
sum+=p;
}
if(c[x][j]>
sum+mn)c[x][j]=
sum+mn;
if(c[x][j]>
sum&&(j>d||j==d&&!ned[x]))c[x][j]=
sum;
}
}
int main()
{
read(n),read(d);
for(
int i=
1;i<=n;i++)
read(c[i][
0]);
read(m);
while(m--)
read(p),ned[p]=
1;
for(
int i=
1;i<n;i++)
read(m),read(p),
add(m,p),add(p,m);
dfs(f[
1]=
1);printf(
"%d",c[
1][d]);
}
题目无关
这道题曾为我带来深重的心理阴影。 插一段本来应属于【jloi2016游记】的东西 在下第一次 在比赛的某一天爆零 是JLOI2016的day1。多么令人怀念(大雾 当时感觉这道题好不可做啊,暴力调了1.5h没调出来,然后弃疗了。 后来查了题解,题解表示【这个dp很好理解】 但是当时在下非常弱,看不懂转移,因此受到了打击。
今天莫名想起来这道题,写了写,1A。(这只是因为以前见过题解…大约