解题:JLOI 2016 侦查守卫

题面

经典的$cov-unc$树形dp(这词是你自己造的吧=。=)

设$cov[i][j]$表示覆盖完$i$的子树后至少向外再覆盖$j$层的最小代价,$unc[i][j]$表示$i$的子树中还剩下至少$j$层没有覆盖时的最小代价,然后是两个数组的抵消转移什么的

1.边界:对于每个需要覆盖的节点$i$,$unc[i][0]=cov[i][0]=cost_i$,对于不需要覆盖的节点$unc[i][0]=cov[i][0]=0$

而对于每个点$i$,又都有$unc[i][j]=0,cov[i][j]=cost_i(j∈N^*\&\&j<=d)$

2.$cov$的转移

对于每个距离$j$,显然有$cov[i][j]+=unc[goal[i]][j]$(覆盖它当前这个子树)

然后对于距离小于$d$的情况有$cov[i][j]=min(cov[i][j],unc[i][j+1]+cov[goal[i]][j+1])$(子树向外覆盖)

还有我们定义的是“至少”:$cov[i][j]=min(cov[i][j],cov[i][j+1])$

3.$unc$的转移

显然的,有$unc[i][0]=cov[i][0]$ 

子树的第$i-1$层被覆盖则自己的第$i$层被覆盖,同时也是注意定义里的“最少”:$unc[i][j]=min(unc[i][j]+unc[goal[i]][j-1],unc[i][j-1]$

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=500005,K=21;
 6 int p[N],noww[2*N],goal[2*N];
 7 int cst[N],imp[N],unc[N][K],cov[N][K];
 8 int n,d,m,t1,t2,cnt,ans=0x3f3f3f3f;
 9 void link(int f,int t)
10 {
11     noww[++cnt]=p[f];
12     goal[cnt]=t,p[f]=cnt;
13 }
14 void DFS(int nde,int fth)
15 {
16     if(imp[nde]) unc[nde][0]=cov[nde][0]=cst[nde];
17     for(int i=1;i<=d;i++) cov[nde][i]=cst[nde];
18     for(int i=p[nde];i;i=noww[i])
19         if(goal[i]!=fth)
20         {
21             DFS(goal[i],nde);
22             for(int j=d;~j;j--)
23             {
24                 cov[nde][j]+=unc[goal[i]][j];
25                 if(j<d)
26                 {
27                     cov[nde][j]=min(cov[nde][j],cov[nde][j+1]);
28                     cov[nde][j]=min(cov[nde][j],cov[goal[i]][j+1]+unc[nde][j+1]);
29                 }
30             }
31             unc[nde][0]=cov[nde][0];
32             for(int j=1;j<=d;j++)
33                 unc[nde][j]=min(unc[nde][j]+unc[goal[i]][j-1],unc[nde][j-1]);
34         }
35 }
36 int main ()
37 {
38     scanf("%d%d",&n,&d);
39     for(int i=1;i<=n;i++)
40         scanf("%d",&cst[i]);
41     scanf("%d",&m);
42     for(int i=1;i<=m;i++)
43         scanf("%d",&t1),imp[t1]=true;
44     for(int i=1;i<n;i++)
45     {
46         scanf("%d%d",&t1,&t2);
47         link(t1,t2),link(t2,t1);
48     }
49     DFS(1,0); 
50     for(int i=0;i<=d;i++) 
51         ans=min(ans,cov[1][i]);
52     printf("%d",ans);
53     return 0;
54 }
View Code

 

转载于:https://www.cnblogs.com/ydnhaha/p/9842141.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值