1. 程式人生 > >jzoj5966. NOIP2018D2T3 保衛王國(動態dp,倍增)

jzoj5966. NOIP2018D2T3 保衛王國(動態dp,倍增)

題面

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));
		}
	}
}