【树链剖分】NOIP2018D2T3 保卫王国

本文探讨了一种使用树链剖分和线段树解决动态DP问题的方法,通过将每个点的两种代价(选与不选)进行维护,实现高效的区间更新和查询。文章详细介绍了算法的具体实现过程,包括数据结构的构建、信息的反馈以及限制条件的处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

分析:

听说是动态Dp的板题,但去年太菜没去冬令营听

仔细想想,用树链剖分也能过,而且的确也不是很难。

每个点有两种代价:选的代价和不选的代价。

线段树维护tree(x,0/1,0/1)分别表示左右端点是否选中。

合并两个线段:
在这里插入图片描述

其实就是分类讨论一下。。

然后保证先处理下层再处理上层,换言之,把每条链的信息反馈到其父亲节点上。
在这里插入图片描述
然后就简单了,每次限制,就把它对偶的选择代价+INF即可。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#define SF scanf
#define PF printf
#define MAXN 100010
#define INF 20000000000ll
using namespace std;
typedef long long ll;
vector<int> a[MAXN];
ll val[MAXN];
int n,m,son[MAXN],siz[MAXN],rnk[MAXN];
void dfs(int x,int f=0){
	siz[x]=1;
	son[x]=-1;
	for(int i=0;i<int(a[x].size());i++){
		int u=a[x][i];
		if(u==f)
			continue;
		dfs(u,x);
		siz[x]+=siz[u];	
		if(son[x]==-1||siz[u]>siz[son[x]])
			son[x]=u;
	}
}
int dfn[MAXN],lft[MAXN],rft[MAXN],cnt,tot;
ll tree[MAXN*4][2][2];
int pl[MAXN*4],pr[MAXN*4],fa[MAXN],rt[MAXN];
ll val0[MAXN],val1[MAXN];
void build(int l,int r,int id){
	if(l==r){
		tree[id][0][0]=val0[rnk[l]];
		tree[id][1][1]=val1[rnk[l]];
		tree[id][0][1]=tree[id][1][0]=INF;
		return ;
	}
	int mid=(l+r)>>1;
	pl[id]=++tot;
	pr[id]=++tot;
	build(l,mid,pl[id]);
	build(mid+1,r,pr[id]);
	tree[id][0][0]=min(tree[pl[id]][0][1]+min(tree[pr[id]][0][0],tree[pr[id]][1][0]),tree[pr[id]][1][0]+min(tree[pl[id]][0][0],tree[pl[id]][0][1]));
	tree[id][0][1]=min(tree[pl[id]][0][1]+min(tree[pr[id]][0][1],tree[pr[id]][1][1]),tree[pr[id]][1][1]+min(tree[pl[id]][0][0],tree[pl[id]][0][1]));
	tree[id][1][0]=min(tree[pl[id]][1][1]+min(tree[pr[id]][0][0],tree[pr[id]][1][0]),tree[pr[id]][1][0]+min(tree[pl[id]][1][0],tree[pl[id]][1][1]));
	tree[id][1][1]=min(tree[pl[id]][1][1]+min(tree[pr[id]][0][1],tree[pr[id]][1][1]),tree[pr[id]][1][1]+min(tree[pl[id]][1][0],tree[pl[id]][1][1]));
//	PF("[%d %d %lld %lld %lld %lld]\n",l,r,tree[id][0][0],tree[id][0][1],tree[id][1][0],tree[id][1][1]);
}
void prepare(int x,int tp,int f=0){
	dfn[x]=++cnt;
	rnk[cnt]=x;
	lft[x]=tp;
	fa[x]=f;
	if(son[x]==-1){
		rft[x]=x;
		if(tp==x){
			rt[x]=++tot;
			build(dfn[x],dfn[rft[x]],rt[x]);
			val0[f]+=min(tree[rt[x]][1][0],tree[rt[x]][1][1]);
			val1[f]+=min(min(tree[rt[x]][1][0],tree[rt[x]][1][1]),min(tree[rt[x]][0][0],tree[rt[x]][0][1]));
		}
		return ;
	}
	prepare(son[x],tp,x);
	for(int i=0;i<int(a[x].size());i++){
		int u=a[x][i];
		if(u==f||u==son[x])
			continue;
		prepare(u,u,x);
	}
	rft[x]=rft[son[x]];
	if(x==tp){
		rt[x]=++tot;
		build(dfn[x],dfn[rft[x]],rt[x]);
		val0[f]+=min(tree[rt[x]][1][0],tree[rt[x]][1][1]);
		val1[f]+=min(min(tree[rt[x]][1][0],tree[rt[x]][1][1]),min(tree[rt[x]][0][0],tree[rt[x]][0][1]));
	}
}
void change(int l,int r,int id,int pos){
	if(l==r){
		tree[id][0][0]=val0[rnk[l]];
		tree[id][1][1]=val1[rnk[l]];
		tree[id][0][1]=tree[id][1][0]=INF;
		return ;
	}
	int mid=(l+r)>>1;
	if(pos<=mid)
		change(l,mid,pl[id],pos);
	if(pos>mid)
		change(mid+1,r,pr[id],pos);
	tree[id][0][0]=min(tree[pl[id]][0][1]+min(tree[pr[id]][0][0],tree[pr[id]][1][0]),tree[pr[id]][1][0]+min(tree[pl[id]][0][0],tree[pl[id]][0][1]));
	tree[id][0][1]=min(tree[pl[id]][0][1]+min(tree[pr[id]][0][1],tree[pr[id]][1][1]),tree[pr[id]][1][1]+min(tree[pl[id]][0][0],tree[pl[id]][0][1]));
	tree[id][1][0]=min(tree[pl[id]][1][1]+min(tree[pr[id]][0][0],tree[pr[id]][1][0]),tree[pr[id]][1][0]+min(tree[pl[id]][1][0],tree[pl[id]][1][1]));
	tree[id][1][1]=min(tree[pl[id]][1][1]+min(tree[pr[id]][0][1],tree[pr[id]][1][1]),tree[pr[id]][1][1]+min(tree[pl[id]][1][0],tree[pl[id]][1][1]));
//	PF("<%d %d %lld(00) %lld(01) %lld(10) %lld(11)>\n",l,r,tree[id][0][0],tree[id][0][1],tree[id][1][0],tree[id][1][1]);
}
void update(int x){
	int f=fa[lft[x]];
	val0[f]-=min(tree[rt[lft[x]]][1][0],tree[rt[lft[x]]][1][1]);
	val1[f]-=min(min(tree[rt[lft[x]]][1][0],tree[rt[lft[x]]][1][1]),min(tree[rt[lft[x]]][0][0],tree[rt[lft[x]]][0][1]));
	change(dfn[lft[x]],dfn[rft[x]],rt[lft[x]],dfn[x]);
	val0[f]+=min(tree[rt[lft[x]]][1][0],tree[rt[lft[x]]][1][1]);
	val1[f]+=min(min(tree[rt[lft[x]]][1][0],tree[rt[lft[x]]][1][1]),min(tree[rt[lft[x]]][0][0],tree[rt[lft[x]]][0][1]));
	if(f==0)
		return ;
	update(f);
}
char s[20];
int main(){
	SF("%d%d%s",&n,&m,s);
	for(int i=1;i<=n;i++)
		SF("%lld",&val1[i]);
	int u,v;
	for(int i=1;i<n;i++){
		SF("%d%d",&u,&v);
		a[u].push_back(v);
		a[v].push_back(u);
	}
	dfs(1);
	prepare(1,1);
	int A,B,x,y;
	for(int i=1;i<=m;i++){
		SF("%d%d%d%d",&A,&x,&B,&y);	
		ll va1=val1[A];
		ll va0=val0[A];
		if(x==0)
			val1[A]+=INF;
		else
			val0[A]+=INF;
		update(A);
		ll vb1=val1[B];
		ll vb0=val0[B];
		if(y==0)
			val1[B]+=INF;
		else
			val0[B]+=INF;
		update(B);
		ll res=min(min(tree[rt[1]][0][0],tree[rt[1]][0][1]),min(tree[rt[1]][1][0],tree[rt[1]][1][1]));
		if(res>=INF)
			res=-1;
		PF("%lld\n",res);
		val1[B]=vb1;
		val0[B]=vb0;
		update(B);
		val1[A]=va1;
		val0[A]=va0;
		update(A);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值