版权声明:未经作者本人允许禁止转载。 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是容易去掉某一部分的贡献的。
其实由于没有修改,不需要树剖,倍增就可以了。
设
表示在点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));
}
}
}