*题解:P5024 [NOIP 2018 提高组] 保卫王国

原题链接

解析

这里介绍倍增的做法。

考虑如果没有要求时怎么做。设 \(f_{i,j}\) 表示以 \(i\) 为根的子树中,点 \(i\) 颜色为 \(j\) 时的最小花费。于是有:

\[\begin{align*} f_{i,0} &= \sum_{j\in son_i} f_{j,1}\\ f_{i,1} &= p_i + \sum_{j\in son_i} \min(f_{j,0},f_{j,1}) \end{align*} \]

考虑钦定一个点时怎么做。由于修改一个点会影响其到根的这条链,所以考虑从上往下 dp。设 \(g_{i,j}\) 表示当点 \(i\) 的颜色为 \(j\) 时,总体最小花费,讨论该点与父结点颜色,则有:

\[\begin{align*} g_{i,0} &= g_{fa_i,1} - \min(f_{i,0},f_{i,1}) + f_{i,0}\\ g_{i,1} &= \min(g_{fa_i,1} - \min(f_{i,0},f_{i,1}) + f_{i,1},g_{fa_i,0} - f_{x,1} + f_{x,1}) \end{align*} \]

接下来考虑钦定两个点,当这两个点相邻时(即性质 2)怎么做。设这两个点为 \(u\)\(fa_u\),这两个点的颜色不会影响 \(u\) 的子树,所以不妨把 \(fa_u\) 的子树直接挖掉,设 \(h_{i,j}\) 表示点 \(fa_i\) 的颜色为 \(j\) 且点 \(i\) 的子树被挖掉时的最小花费,那么有:

\[\begin{align*} h_{i,0}&=g_{fa_i,0} - f_{i,1}\\ h_{i,1}&=g_{fa_i,1}- \min(f_{i,0},f_{i,1}) \end{align*} \]

处理完 \(fa_u\) 的子树后再进行合并。对于 \(fa_u\) 的子树内部,讨论 \(u\)\(fa_u\) 的染色情况即可。

再来就是钦定两个点但这两个点在其中一个点到根的链上的情况。此时两点之间会有其余点,我们需要通过这些点将限制传递。设 \(v_{i,x,k,l}\) 表示在从 \(i\) 点往上走 \(x\) 步到达的点 \(j\) 的子树中,点 \(i\) 的颜色为 \(k\),点 \(j\) 的颜色为 \(l\) 的最小花费。然而状态数太多,所以改为倍增跳,点 \(j\) 变为从 \(i\) 点往上走 \(2 ^ x - 1\) 步到达的节点。如果设 \(fa_{i,j}\) 表示点 \(i\)\(2^j\) 级祖先,则有:

\[v_{i,x,k,l}=\min(v_{i,x - 1,k,0/1} + v_{fa_{i,x - 1},x - 1,0/1,l}) \]

然后就变成性质 2 了。

接下来轮到正解。两点 \(a,b\) 可能不在一条到根的链上,聪明的你肯定想到了挖掉以 LCA 为根的子树。对于 \(a\to b\) 的链,将 \(v\) 的信息在 LCA 处合并即可。

时间复杂度 \(O((n + m) \log n )\)

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 100000 + 5,M = 17;
typedef long long ll;
typedef pair<int,int> pii;
ll f[N][2],g[N][2],h[N][2],v[N][M][2][2],h2[N][2];
int fa[N][M],dep[N],ffa[N][M];
int p[N];
int n,m;
vector<int> t[N];
void dfs(int x,int _f){
	dep[x] = dep[_f] + 1;
	fa[x][0] = _f;
	ffa[x][0] = x;
	ffa[x][1] = _f;
	for(int i=1;i<M;i++){
		fa[x][i] = fa[fa[x][i - 1]][i - 1];
		ffa[x][i] = ffa[fa[ffa[x][i - 1]][0]][i - 1];//x 的 (2^i) - 1 级祖先 
	}
	f[x][1] = p[x];
	for(int nx : t[x])if(nx != _f){
		dfs(nx,x);
		f[x][0] += f[nx][1]; 
		f[x][1] += min(f[nx][0],f[nx][1]);
	}
}
void dfs2(int x){
	int ff = fa[x][0];
	g[x][0] = g[ff][1] - min(f[x][0],f[x][1]) + f[x][0];
	g[x][1] = min(g[ff][1] - min(f[x][0],f[x][1]) + f[x][1],g[ff][0] - f[x][1] + f[x][1]);
	h[x][0] = g[ff][0] - f[x][1];
	h[x][1] = g[ff][1] - min(f[x][0],f[x][1]);
	h2[x][0] = f[x][1];
	h2[x][1] = min(f[x][0],f[x][1]);
	if(x == 1){
		g[x][0] = f[x][0];
		g[x][1] = f[x][1];
		h[x][0] = 0;
		h[x][1] = 0;
		h2[x][0] = f[x][0];
	}
	for(int nx : t[x])if(nx != ff){
		dfs2(nx);
	}
}
void init(){
	for(int i=1;i<=n;i++){
		v[i][0][0][0] = f[i][0];
		v[i][0][1][1] = f[i][1];
		v[i][0][1][0] = v[i][0][0][1] = 2e10;
	}
	for(int j=1;j<M;j++){
		for(int i=1;i<=n;i++)if(ffa[i][j]){
			for(int x=0;x<=1;x++){
				for(int y=0;y<=1;y++){	
					int f0 = fa[i][j - 1],f1 = ffa[i][j - 1];
					ll l0 = v[i][j - 1][x][0],l1 = v[i][j - 1][x][1],r0 = v[f0][j - 1][0][y],r1 = v[f0][j - 1][1][y];
					v[i][j][x][y] = min({(ll)2e10,l0 + r1 - h2[f1][1],
										 l1 + r1 - h2[f1][1],
										 l1 + r0 - h2[f1][0]});
				}
			}
		}
	}
}
struct S{
	ll r0,r1,lca;
};
void upd(ll res[],int u,int i){
	ll r1 = res[1],r0 = res[0];
	int fu = fa[u][0];
	res[1] = min(min(r0,r1) + v[fu][i][1][1] - h2[u][1],r1 + v[fu][i][0][1] - h2[u][0]);
	res[0] = min(min(r0,r1) + v[fu][i][1][0] - h2[u][1],r1 + v[fu][i][0][0] - h2[u][0]);
}
S get(int a,int x,int b,int y){
	if(dep[a] > dep[b]){
		swap(a,b);
		swap(x,y);
	}
	ll resa[2] = {v[a][0][0][0],v[a][0][1][1]},resb[2] = {v[b][0][0][0],v[b][0][1][1]};
	resa[x ^ 1] = resb[y ^ 1] = 2e10;
	for(int i=M - 1;i>=0;i--){
		if(dep[fa[b][i]] >= dep[a]){
			upd(resb,b,i);
			b = fa[b][i];
		}
	}
	if(a == b){
		return {resb[x],resb[x],a};
	}
	for(int i=M - 1;i>=0;i--){
		if(fa[a][i] != fa[b][i]){
			upd(resb,b,i);
			upd(resa,a,i);
			a = fa[a][i],b = fa[b][i];
		}
	}
	upd(resa,a,0);
	ll res[2] = {resa[0] + resb[1] - h2[b][0],min(resa[1] + resb[0] - h2[b][1],resa[1] + resb[1] - h2[b][1])};
	return {res[0],res[1],fa[a][0]};
}
ll sol(int a,int x,int b,int y){
	if(dep[a] > dep[b]){
		swap(a,b);
		swap(x,y);
	}
	S res = get(a,x,b,y);
	ll r = 2e10;
	if(res.lca == a){
		if(x == 1){
			return min(h[res.lca][0] + res.r1,h[res.lca][1] + res.r1);
		}else{
			return h[res.lca][1] + res.r0;
		}
	}
	return min({h[res.lca][1] + res.r0,h[res.lca][0] + res.r1,h[res.lca][1] + res.r1});
}
int main(){
	ios::sync_with_stdio(false);
	cin.tie(0);
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	string type;
	cin>>n>>m>>type;
	for(int i=1;i<=n;i++){
		cin>>p[i];
	}
	for(int i=1;i<n;i++){
		int u,v;
		cin>>u>>v;
		t[u].push_back(v);
		t[v].push_back(u);
	}
	dfs(1,0);
	dfs2(1);
	init(); 
	while(m--){
		int a,x,b,y;
		cin>>a>>x>>b>>y;
		ll res = sol(a,x,b,y);
		cout<<(res > 1e10 ? -1 : res)<<'\n';
	}
	return 0;
}

posted @ 2026-04-18 00:09  yutar  阅读(16)  评论(0)    收藏  举报