jzoj5966. NOIP2018D2T3 保卫王国(动态dp,倍增)

版权声明:未经作者本人允许禁止转载。 https://blog.csdn.net/jokerwyt/article/details/84107967

题面

Z国有n座城市,n-1条双向道路,每条双向道路连接两座城市,且任意两座城市都能通过若干条道路相互到达。
Z国的国防部长小Z要在城市中驻扎军队。驻扎军队需要满足如下几个条件:
①一座城市可以驻扎一支军队,也可以不驻扎军队。
②由道路直接连接的两座城市中至少要有一座城市驻扎军队。
③在城市里驻扎军队会产生花费,在编号为i的城市中驻扎军队的花费是pi。
小Z很快就规划出了一种驻扎军队的方案,使总花费最小。但是国王又给小Z提出了m个要求,每个要求规定了其中两座城市是否驻扎军队。小Z需要针对每个要求逐一给出回答。具体而言,如果国王提出的第j个要求能够满足上述驻扎条件(不需要考虑第j个要求之外的其它要求),则需要给出在此要求前提下驻扎军队的最小开销。如果国王提出的第j个要求无法满足,则需要输出-1(1<=j<=m) 。现在请你来帮助小Z。
在这里插入图片描述

本身还有1.5h的时候想到了大概思路,不过只想到了树剖怎么做。估摸了一下代码长度,不敢打啊!!!

首先要注意到他这个dp是容易去掉某一部分的贡献的。

其实由于没有修改,不需要树剖,倍增就可以了。
w [ x ] [ i ] [ 0 / 1 ] [ 0 / 1 ] w[x][i][0/1][0/1] 表示在点x的2^i级祖先这颗子树中,保证x的状态为啥,祖先的状态为啥的,不包括x子树的,最小答案。

询问的时候,倍增到lca下一个点,然后再合并一下就可以了。
合并的时候,由于考虑了一个重复点,所以就可以直接对应状态加起来取min就可以了。
具体见代码吧。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define min(a,b)  ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
const ll inf = 1e18;
int n,m;
char S[100];
int p[N],to[N*2],nex[N*2],tot,final[N];

ll rf[N][2],f[N][2];

struct statu{
	ll a[2][2];
} w[N][17];

void link(int x,int y) {
	to[++tot] = y, nex[tot] = final[x], final[x] = tot;
}

int fa[N],dep[N],g[N][17];
void dfs(int x){
	dep[x] = dep[fa[x]] + 1;
	g[x][0] = fa[x];
	for (int i = 1; i < 17; i++)
		g[x][i] = g[g[x][i-1]][i-1];
	
	f[x][0] = 0;
	f[x][1] = p[x];
	for (int i = final[x]; i; i=nex[i]) {
		int y = to[i];
		if (y != fa[x]) {
			fa[y] = x;
			dfs(y);
			f[x][0] += f[y][1];
			f[x][1] += min(f[y][1],f[y][0]);
		}
	}
}

void dfs2(int x,ll f0,ll f1) {
	rf[x][0] = f[x][0] + f1;
	rf[x][1] = f[x][1] + min(f0, f1);
	for (int i = final[x]; i; i=nex[i]) {
		int y = to[i]; if (y != fa[x]) {
			w[y][0].a[0][0] = inf;
			w[y][0].a[1][0] = f[x][0] - f[y][1];
			w[y][0].a[0][1] = w[y][0].a[1][1] = f[x][1] - min(f[y][0], f[y][1]);
			dfs2(y, rf[x][0] - f[y][1], rf[x][1] - min(f[y][0], f[y][1]));
		}
	}
}

statu C;
statu merge(const statu &A, const statu &B) {
	memset(C.a,127/2,sizeof C.a);
	for (int a = 0; a < 2; a++) {
		for (int b = 0; b < 2; b++) {
			for (int c = 0; c < 2; c++) {
				C.a[a][b] = min(C.a[a][b], A.a[a][c] + B.a[c][b]);
			}
		}
	}
	return C;
}

int la,lb,sa,sb;
int main() {
	freopen("defense.in","r",stdin);
	freopen("defense.out","w",stdout);
	cin>>n>>m>>S;
	for (int i = 1; i <= n; i++) scanf("%d",&p[i]);
	for (int i = 1; i < n; i++) {
		int u,v; scanf("%d %d",&u,&v);
		link(u,v),link(v,u);
	}
	
	dfs(1);
	dfs2(1,0,0);
	for (int i = 1; i <= 16; i++) {
		for (int j = 1; j <= n; j++) {
			w[j][i] = merge(w[j][i-1], w[g[j][i-1]][i-1]);
		}
	}
	for (int i = 1; i <= m; i++) {
		scanf("%d %d %d %d",&la,&sa,&lb,&sb);
		if (sa + sb == 0 && (fa[la] == lb || fa[lb] == la)) {
			printf("-1\n"); continue;
		}
		if (dep[la]<dep[lb]) swap(la,lb),swap(sa,sb);
		int ta=la,tb=lb;
		statu A,B;
		memset(A.a,127/2,sizeof A.a);
		A.a[sa][sa]=f[la][sa];
		for (int i = 16; ~i; i--) if (dep[g[ta][i]] > dep[tb]){
			A=merge(A,w[ta][i]);
			ta=g[ta][i];
		}
		if (fa[ta] == tb) {
			ll cq0 = rf[tb][0] - f[ta][1];
			ll cq1 = rf[tb][1] - min(f[ta][0], f[ta][1]);
			
			cq0 += A.a[sa][1];
			cq1 += min(A.a[sa][1], A.a[sa][0]);
			printf("%lld\n",sb==0?cq0:cq1);
			
		} else {
			A = merge(A, w[ta][0]); ta = fa[ta];
			memset(B.a,127/2,sizeof B.a);
			B.a[sb][sb] = f[lb][sb];
			for (int i = 16; ~i ;i--) if (g[ta][i] != g[tb][i]){
				A=merge(A,w[ta][i]);
				B=merge(B,w[tb][i]);
				ta=g[ta][i],tb=g[tb][i];
			}
			int lca = fa[ta];
			ll cq0 = rf[lca][0] - f[ta][1] - f[tb][1];
			ll cq1 = rf[lca][1] - min(f[ta][0],f[ta][1]) - min(f[tb][0],f[tb][1]);
			
			cq0 += A.a[sa][1] + B.a[sb][1];
			cq1 += min(A.a[sa][1], A.a[sa][0]) + min(B.a[sb][0], B.a[sb][1]);
			printf("%lld\n",min(cq0,cq1));
		}
	}
}

猜你喜欢

转载自blog.csdn.net/jokerwyt/article/details/84107967