[NOIP 2018 提高组] 保卫王国

[NOIP 2018 提高组] 保卫王国

比较不错的动态 dp 综合题。

题意

给定一棵 \(n\) 个节点的带点权的树,第 \(i\) 个点的点权是 \(p_i\)

现在有 \(m\) 个询问,每次询问给定两个点 \(a\)\(b\) 并指定这两个点选或不选,求树的最小权点覆盖。

\(1 \leq n,m \leq 10^5\)\(1 \leq p_i \leq 10^5\)

思路

先判断无解情况。

题目有强制选点,可以通过把点权设为正无穷或负无穷实现。

接下来考虑如何求出最小权点覆盖。

最小权点覆盖加最大权独立集等于全集,所以只需要求出最大权独立集即可。

注意细节,可能会造成精神污染。

代码

#include<iostream>
#include<cstdio>
#include<vector>
#include<unordered_map>
using namespace std;
const long long INF=0x3f3f3f3f3f3f3f3f,inf_max=0x3f3f3f3f3f3f,inf_min=-0x3f3f3f3f3f3f;
int tot,son[100010],pa[100010];
int dfn[100010],top[100010],rev[100010],bottom[100010],child[100010];
long long num[100010],dp_f[100010][2],dp_g[100010][2];
vector<int> G[100010],T[100010];
void dfs1(int u,int fa){
	child[u]=1;
	for(int i=0;i<G[u].size();i++){
		int v=G[u][i];
		if(v!=fa){
			T[u].push_back(v);
			pa[v]=u;
			dfs1(v,u);
			child[u]+=child[v];
			if(child[v]>=child[son[u]]){
				son[u]=v;
			}
		}
	}
}
void dfs2(int u,int fa){
	rev[++tot]=u;
	dfn[u]=tot;
	top[u]=fa;
	bottom[fa]=u;
	if(son[u]){
		dfs2(son[u],fa);
	}
	for(int i=0;i<T[u].size();i++){
		int v=T[u][i];
		if(v!=son[u]){
			dfs2(v,v);
		}
	}
}
void dfs3(int u){
	dp_f[u][1]=dp_g[u][1]=num[u];
	for(int i=0;i<T[u].size();i++){
		int v=T[u][i];
		dfs3(v);
		dp_f[u][0]+=max(dp_f[v][0],dp_f[v][1]);
		dp_f[u][1]+=dp_f[v][0];
		if(v!=son[u]){
			dp_g[u][0]+=max(dp_f[v][0],dp_f[v][1]);
			dp_g[u][1]+=dp_f[v][0];
		}
	}
}
struct Matrix{
	long long matrix[2][2];
};
const Matrix operator *(const Matrix &x,const Matrix &y){
	Matrix z;
	for(int i=0;i<2;i++){
		for(int j=0;j<2;j++){
			z.matrix[i][j]=max(x.matrix[i][0]+y.matrix[0][j],x.matrix[i][1]+y.matrix[1][j]);
		}
	}
	return z;
}
struct Node{
	int l,r;
	Matrix g;
}a[400010];
void pushup(int id){
	a[id].g=a[id*2].g*a[id*2+1].g;
}
void build(int id,int l,int r){
	a[id].l=l;
	a[id].r=r;
	if(a[id].l==a[id].r){
		a[id].g.matrix[0][0]=a[id].g.matrix[0][1]=dp_g[rev[l]][0];
		a[id].g.matrix[1][0]=dp_g[rev[l]][1];
		a[id].g.matrix[1][1]=-INF;
	}
	else{
		int mid=(l+r)>>1;
		build(id*2,l,mid);
		build(id*2+1,mid+1,r);
		pushup(id);
	}
}
Matrix query(int id,int l,int r){
	if(l<=a[id].l  &&  a[id].r<=r){
		return a[id].g;
	}
	bool flag=false;
	Matrix ans;
	if(l<=a[id*2].r){
		flag=true;
		ans=query(id*2,l,r);
	}
	if(a[id*2+1].l<=r){
		if(flag==false){
			ans=query(id*2+1,l,r);
		}
		else{
			ans=ans*query(id*2+1,l,r);
		}
	}
	return ans;
}
void modify(int id,int pos,Matrix dif){
	if(a[id].l==a[id].r){
		a[id].g=dif;
		return ;
	}
	if(pos<=a[id*2].r){
		modify(id*2,pos,dif);
	}
	else{
		modify(id*2+1,pos,dif);
	}
	pushup(id);
}
struct Query{
	long long dp0,dp1;
};
Query query_f(int u){
	int id_u=dfn[u],id_bottom=dfn[bottom[u]];
	Matrix tmp=query(1,id_bottom,id_bottom),tmp2=query(1,id_u,id_bottom-1);
	Query tmp3=(Query){tmp.matrix[0][0],tmp.matrix[1][0]};
	if(id_bottom==id_u) return tmp3;
	Query ans=(Query){max(tmp2.matrix[0][0]+tmp3.dp0,tmp2.matrix[0][1]+tmp3.dp1),max(tmp2.matrix[1][0]+tmp3.dp0,tmp2.matrix[1][1]+tmp3.dp1)};
	return ans;
} 
unordered_map<int,unordered_map<int,bool> > road;
int main(){
	int n,m;
	scanf("%d %d",&n,&m);
	string subtask;
	cin>>subtask;
	long long sum=0;
	for(int i=1;i<=n;i++){
		scanf("%lld",&num[i]);
		sum+=num[i];
	}
	for(int i=1;i<n;i++){
		int u,v;
		scanf("%d %d",&u,&v);
		G[u].push_back(v);
		G[v].push_back(u);
		road[u][v]=road[v][u]=true;
	}
	dfs1(1,-1);
	dfs2(1,1);
	dfs3(1);
	for(int i=1;i<=n;i++){
		bottom[i]=bottom[top[i]];
	}
	build(1,1,n);
	while(m--){
		int u1,d1,u2,d2;
		scanf("%d %d %d %d",&u1,&d1,&u2,&d2);
		if(road[u1][u2]  &&  d1==0  &&  d2==0){
			printf("-1\n");
			continue;
		}
		long long dif=0;
		long long tmp_u1=u1,lst_num_u1=num[u1],dif_num_u1;
		if(d1==0){
			dif_num_u1=inf_max;
			dif+=num[u1]-inf_max;
		}
		else dif_num_u1=inf_min;
		Query lst=query_f(top[u1]); 
		Matrix tmp=query(1,dfn[u1],dfn[u1]);
		tmp.matrix[1][0]+=dif_num_u1-num[u1];
		num[u1]=dif_num_u1;
		modify(1,dfn[u1],tmp);
		while(top[u1]!=1){
			int pre=top[u1];
			Query f=query_f(pre);
			int fa=pa[pre];
			tmp=query(1,dfn[fa],dfn[fa]);
			tmp.matrix[0][0]+=max(f.dp0,f.dp1)-max(lst.dp0,lst.dp1);
			tmp.matrix[0][1]+=max(f.dp0,f.dp1)-max(lst.dp0,lst.dp1);
			tmp.matrix[1][0]+=f.dp0-lst.dp0;
			lst=query_f(top[fa]);
			modify(1,dfn[fa],tmp);
			u1=fa;
		}
		long long tmp_u2=u2,lst_num_u2=num[u2],dif_num_u2;
		if(d2==0){
			dif_num_u2=inf_max;
			dif+=num[u2]-inf_max;
		}
		else dif_num_u2=inf_min;
		lst=query_f(top[u2]); 
		tmp=query(1,dfn[u2],dfn[u2]);
		tmp.matrix[1][0]+=dif_num_u2-num[u2];
		num[u2]=dif_num_u2;
		modify(1,dfn[u2],tmp);
		while(top[u2]!=1){
			int pre=top[u2];
			Query f=query_f(pre);
			int fa=pa[pre];
			tmp=query(1,dfn[fa],dfn[fa]);
			tmp.matrix[0][0]+=max(f.dp0,f.dp1)-max(lst.dp0,lst.dp1);
			tmp.matrix[0][1]+=max(f.dp0,f.dp1)-max(lst.dp0,lst.dp1);
			tmp.matrix[1][0]+=f.dp0-lst.dp0;
			lst=query_f(top[fa]);
			modify(1,dfn[fa],tmp);
			u2=fa;
		}
		lst=query_f(1);
		printf("%lld\n",sum-max(lst.dp0,lst.dp1)-dif);
		u1=tmp_u1;
		lst=query_f(top[u1]); 
		tmp=query(1,dfn[u1],dfn[u1]);
		tmp.matrix[1][0]+=lst_num_u1-num[u1];
		num[u1]=lst_num_u1;
		modify(1,dfn[u1],tmp);
		while(top[u1]!=1){
			int pre=top[u1];
			Query f=query_f(pre);
			int fa=pa[pre];
			tmp=query(1,dfn[fa],dfn[fa]);
			tmp.matrix[0][0]+=max(f.dp0,f.dp1)-max(lst.dp0,lst.dp1);
			tmp.matrix[0][1]+=max(f.dp0,f.dp1)-max(lst.dp0,lst.dp1);
			tmp.matrix[1][0]+=f.dp0-lst.dp0;
			lst=query_f(top[fa]);
			modify(1,dfn[fa],tmp);
			u1=fa;
		}
		u2=tmp_u2;
		lst=query_f(top[u2]); 
		tmp=query(1,dfn[u2],dfn[u2]);
		tmp.matrix[1][0]+=lst_num_u2-num[u2];
		num[u2]=lst_num_u2;
		modify(1,dfn[u2],tmp);
		while(top[u2]!=1){
			int pre=top[u2];
			Query f=query_f(pre);
			int fa=pa[pre];
			tmp=query(1,dfn[fa],dfn[fa]);
			tmp.matrix[0][0]+=max(f.dp0,f.dp1)-max(lst.dp0,lst.dp1);
			tmp.matrix[0][1]+=max(f.dp0,f.dp1)-max(lst.dp0,lst.dp1);
			tmp.matrix[1][0]+=f.dp0-lst.dp0;
			lst=query_f(top[fa]);
			modify(1,dfn[fa],tmp);
			u2=fa;
		}
	}
	return 0;
}
posted @ 2025-05-20 13:13  Oken喵~  阅读(6)  评论(0)    收藏  举报