jzoj5966. NOIP2018D2T3 保衛王國(動態dp,倍增)
阿新 • • 發佈:2018-12-02
題面
Z國有n座城市,n-1條雙向道路,每條雙向道路連線兩座城市,且任意兩座城市都能通過若干條道路相互到達。
Z國的國防部長小Z要在城市中駐紮軍隊。駐紮軍隊需要滿足如下幾個條件:
①一座城市可以駐紮一支軍隊,也可以不駐紮軍隊。
②由道路直接連線的兩座城市中至少要有一座城市駐紮軍隊。
③在城市裡駐紮軍隊會產生花費,在編號為i的城市中駐紮軍隊的花費是pi。
小Z很快就規劃出了一種駐紮軍隊的方案,使總花費最小。但是國王又給小Z提出了m個要求,每個要求規定了其中兩座城市是否駐紮軍隊。小Z需要針對每個要求逐一給出回答。具體而言,如果國王提出的第j個要求能夠滿足上述駐紮條件(不需要考慮第j個要求之外的其它要求),則需要給出在此要求前提下駐紮軍隊的最小開銷。如果國王提出的第j個要求無法滿足,則需要輸出-1(1<=j<=m) 。現在請你來幫助小Z。
本身還有1.5h的時候想到了大概思路,不過只想到了樹剖怎麼做。估摸了一下程式碼長度,不敢打啊!!!
首先要注意到他這個dp是容易去掉某一部分的貢獻的。
其實由於沒有修改,不需要樹剖,倍增就可以了。
設
表示在點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));
}
}
}