BZOJ 4557 侦察守卫

DP

\(F_{i,j}\) 表示处理掉以 \(i\) 为根的子树,并向上支援 \(j\) 格的最小代价

\(j\in [-D,D]\)

暴力转移 \(O(ND^3)\) ~ \(O(ND^2)\)

稍作修改

\(F_{i,j}\) 表示处理掉以 \(i\) 为根的子树,并向上支援至少 \(j\) 格的最小代价

容易得到 \(O(ND)\) 做法

#include <cstdio>
#include <vector>
#include <algorithm>

using std::min;
using std::vector;

const int MAXN=500111;
const int MAXD=23;
const int INF=1034567890;

int N, D, M;

struct Vert{
	int Val;
	bool Is;
	int FE;
	int F_[MAXD<<1], *F;
	Vert(){F=F_+MAXD;}
} V[MAXN];

struct Edge{
	int y, next;
} E[MAXN<<1];

int Ecnt;

void addE(int a, int b){
	++Ecnt;
	E[Ecnt].y=b;E[Ecnt].next=V[a].FE;V[a].FE=Ecnt;
}

void DFS(int at, int f){
	vector<int> Son;
	for(int k=V[at].FE, to;k;k=E[k].next){
		to=E[k].y;
		if(to==f)	continue;
		Son.push_back(to);
		DFS(to, at);
	}
	if(!Son.size()){
		for(int i=1-V[at].Is;i<=D;++i)	V[at].F[i]=V[at].Val;
		return;
	}
	//j=D;
	for(int i=0, s=Son.size();i<s;++i){
		V[at].F[D]+=V[Son[i]].F[-D];
	}
	V[at].F[D]+=V[at].Val;
	//j=[1,D);
	for(int j=!V[at].Is, t;j<D;++j){
		t=0;
		for(int i=0, s=Son.size();i<s;++i){
			t+=V[Son[i]].F[-j];
		}
		V[at].F[j]=V[Son[0]].F[j+1]-V[Son[0]].F[-j]+t;
		for(int i=1, s=Son.size();i<s;++i){
			V[at].F[j]=min(V[at].F[j], V[Son[i]].F[j+1]-V[Son[i]].F[-j]+t);
		}
	}
	//j=0;
	if(!V[at].Is)
		for(int i=0, s=Son.size();i<s;++i)
			V[at].F[0]+=V[Son[i]].F[0];
	//j=-1;
	if(!V[at].Is)	V[at].F[-1]=INF;
	//j=[-D,-1);
	for(int j=-2+V[at].Is;j>=-D;--j){
		for(int i=0, s=Son.size();i<s;++i)
			V[at].F[j]+=V[Son[i]].F[j+1];
	}
	for(int i=D-1;i>=-D;--i)	V[at].F[i]=min(V[at].F[i+1], V[at].F[i]);
}

int main(){
	
	scanf("%d%d", &N, &D);
	
	for(int i=1;i<=N;++i)	scanf("%d", &V[i].Val);
	
	scanf("%d", &M);
	
	for(int i=1, a;i<=M;++i)	{scanf("%d", &a);V[a].Is=true;}
	
	for(int i=1, a, b;i<N;++i){
		scanf("%d%d", &a, &b);
		addE(a, b);addE(b, a);
	}
	
	DFS(1, 0);
	
	printf("%d\n", V[1].F[0]);
	
	return 0;
}

/*
12 2
8 9 12 6 1 1 5 1 4 8 10 6
10
1 2 3 5 6 7 8 9 10 11
1 3
2 3
3 4
4 5
4 6
4 7
7 8
8 9
9 10
10 11
11 12

10

*/

posted @ 2019-03-02 12:14  Pickupwin  阅读(141)  评论(0编辑  收藏  举报