[NOIP2018]保卫王国(动态DP)

[NOIP2018]保卫王国(动态DP)

题面

给出一棵树,有m组询问,每次询问给出两个点,规定他们必须选或必须不选。求树的最小权覆盖集。

分析

此题有倍增+树形dp的做法,常数非常优秀,但思路比较难想到。

显然最小权覆盖集=总点权和-最大权独立集
看到最大权独立集,我们想到板子题[LuoguP4719][模板]动态DP.

考虑如何处理询问。由于我们要权值最小,如果必须选某个点,就把它的点权修改为\(-\infty\),如果必须不选,就修改为\(+\infty\).代码实现上就把它修改成一个较大的数即可,如\(10^{10}\).然后用板子求最大权独立集,再用总和减去。注意当我们把点权修改为\(-\infty\)时,最小权覆盖集会包含\(-\infty\),这时算出的和并不是真正答案,还要加上\(v_x-(-\infty)\),其中\(v_x\)是被强制选的值。

常数较大,注意底层优化。

代码

树剖:

#include<iostream>
#include<cstdio>
#include<cstring>
#define INF 0x3f3f3f3f3f3f3f3f
#define MAXX 1e10
#define maxn 200000
using namespace std;
typedef long long ll;
int n,m;
struct edge {
	int from;
	int to;
	int next;
} E[maxn*2+5];
int head[maxn+5];
int esz=1;
void add_edge(int u,int v) {
	esz++;
	E[esz].from=u;
	E[esz].to=v;
	E[esz].next=head[u];
	head[u]=esz;
}
int fa[maxn+5],son[maxn+5],sz[maxn+5],top[maxn+5],btm[maxn+5],dfn[maxn+5],hash_dfn[maxn+5];
void dfs1(int x,int f) {
	sz[x]=1;
	fa[x]=f;
	for(int i=head[x]; i; i=E[i].next) {
		int y=E[i].to;
		if(y!=f) {
			dfs1(y,x);
			sz[x]+=sz[y];
			if(sz[y]>sz[son[x]]) son[x]=y;
		}
	}
}
int tim=0;
void dfs2(int x,int t) {
	top[x]=t;
	dfn[x]=++tim;
	hash_dfn[dfn[x]]=x;
	if(son[x]) {
		dfs2(son[x],t);
		btm[x]=btm[son[x]];//维护重链最底端节点
	} else btm[x]=x;
	for(int i=head[x]; i; i=E[i].next) {
		int y=E[i].to;
		if(y!=fa[x]&&y!=son[x]) {
			dfs2(y,y);
		}
	}
}


struct matrix {
	ll a[2][2];
	inline void set(ll x) {
		for(int i=0; i<2; i++) {
			for(int j=0; j<2; j++) a[i][j]=x;
		}
	}
	friend matrix operator * (matrix p,matrix q) {
		matrix ans;
		ans.set(-INF);
		for(int i=0; i<2; i++) {
			for(int j=0; j<2; j++) {
				for(int k=0; k<2; k++) {
					ans.a[i][j]=max(ans.a[i][j],p.a[i][k]+q.a[k][j]);
				}
			}
		}
		return ans;
	}
} mat[maxn+5];
ll val[maxn+5];
ll f[maxn+5][2],g[maxn+5][2];
void dfs3(int x) {
	f[x][0]=0;
	f[x][1]=val[x];
	for(int i=head[x]; i; i=E[i].next) {
		int y=E[i].to;
		if(y!=fa[x]) {
			dfs3(y);
			f[x][0]+=max(f[y][0],f[y][1]);
			f[x][1]+=f[y][0];
		}
	}
	g[x][0]=0,g[x][1]=val[x];
	for(int i=head[x]; i; i=E[i].next) {
		int y=E[i].to;
		if(y!=fa[x]&&y!=son[x]) {
			g[x][0]+=max(f[y][0],f[y][1]);
			g[x][1]+=f[y][0];
		}
	}
	mat[x].a[0][0]=g[x][0];
	mat[x].a[0][1]=g[x][0];
	mat[x].a[1][0]=g[x][1];
	mat[x].a[1][1]=-INF;
}

struct segment_tree {
	struct node {
		int l;
		int r;
		matrix v;
	} tree[maxn*4+5];
	void push_up(int pos) {
		tree[pos].v=tree[pos<<1].v*tree[pos<<1|1].v;
	}
	void build(int l,int r,int pos) {
		tree[pos].l=l;
		tree[pos].r=r;
		if(l==r) {
			tree[pos].v=mat[hash_dfn[l]];
			return;
		}
		int mid=(l+r)>>1;
		build(l,mid,pos<<1);
		build(mid+1,r,pos<<1|1);
		push_up(pos);
	}
	void update(int upos,matrix &uval,int pos) {
		if(tree[pos].l==tree[pos].r) {
			tree[pos].v=uval;
			return;
		}
		int mid=(tree[pos].l+tree[pos].r)>>1;
		if(upos<=mid) update(upos,uval,pos<<1);
		else update(upos,uval,pos<<1|1);
		push_up(pos);
	}
	matrix query(int L,int R,int pos) {
		if(L<=tree[pos].l&&R>=tree[pos].r) return tree[pos].v;
		int mid=(tree[pos].l+tree[pos].r)>>1;
		matrix ans;
		ans.a[0][0]=ans.a[1][1]=0;
		ans.a[0][1]=ans.a[1][0]=-INF;
		if(L<=mid) ans=ans*query(L,R,pos<<1);
		if(R>mid) ans=ans*query(L,R,pos<<1|1);
		return ans;
	}
} T;
ll sum=0;

pair<ll,ll> get_f(int x) {
	//f[x]需要从x所在重链底端推上来,变成区间矩阵乘法
	matrix tmp=T.query(dfn[x],dfn[btm[x]],1);
	return make_pair(tmp.a[0][0],tmp.a[1][0]);
}
void change(int x,ll v) {
	sum+=v-val[x];
	g[x][1]+=v-val[x];
	val[x]=v;
	while(x) {
		mat[x].a[0][0]=g[x][0];
		mat[x].a[0][1]=g[x][0];
		mat[x].a[1][0]=g[x][1];
		mat[x].a[1][1]=-INF;
		T.update(dfn[x],mat[x],1);
		x=top[x];
		g[fa[x]][0]-=max(f[x][0],f[x][1]);
		g[fa[x]][1]-=f[x][0];
		pair<ll,ll>tmp=get_f(x);
		f[x][0]=tmp.first;
		f[x][1]=tmp.second;
		g[fa[x]][0]+=max(f[x][0],f[x][1]);
		g[fa[x]][1]+=f[x][0];
		x=fa[x];
	}
}
ll query(int x,int tx,int y,int ty) {
	ll delta=0;
	ll tmpx=val[x],tmpy=val[y];
	if(tx==1) {
		delta+=val[x]+MAXX;
		change(x,-MAXX);
	} else {
		change(x,MAXX);
	}
	if(ty==1) {
		delta+=val[y]+MAXX;
		change(y,-MAXX);
	} else {
		change(y,MAXX);
	}
	pair<ll,ll> tmp=get_f(1);
	ll maxuni=max(tmp.first,tmp.second);
	ll ans=sum-maxuni+delta;
	if(ans>=MAXX) ans=-1;
	change(x,tmpx);
	change(y,tmpy);
	return ans;
}

int main() {
	int u,v,tu,tv;
	char nouse[5];
	scanf("%d %d %s",&n,&m,nouse);
	for(int i=1; i<=n; i++) {
		scanf("%lld",&val[i]);
		sum+=val[i];
	}
	for(int i=1; i<n; i++) {
		scanf("%d %d",&u,&v);
		add_edge(u,v);
		add_edge(v,u);
	}
	dfs1(1,0);
	dfs2(1,1);
	dfs3(1);
	T.build(1,n,1);
	for(int i=1; i<=m; i++) {
		scanf("%d %d %d %d",&u,&tu,&v,&tv);
		printf("%lld\n",query(u,tu,v,tv));
	}
}

LCT:

#include<iostream>
#include<cstdio>
#include<cstring>
#define INF 0x3f3f3f3f3f3f3f3f
#define MAXX 1e10
#define maxn 200000
using namespace std;
typedef long long ll;
template<typename T> void qread(T &x){
	x=0;
	T sign=1;
	char c=getchar();
	while(c<'0'||c>'9'){
		if(c=='-') sign=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9'){
		x=x*10+c-'0';
		c=getchar();
	}
	x=x*sign; 
} 
template<typename T> void qprint(T x){
	if(x<0){
		putchar('-');
		qprint(-x);
	}else if(x==0){
		putchar('0');
		return;
	}else{
		if(x>=10) qprint(x/10);
		putchar('0'+x%10);
	}
}

int n,m;
struct edge {
	int from;
	int to;
	int next;
} E[maxn*2+5];
int head[maxn+5];
int esz=1;
void add_edge(int u,int v) {
	esz++;
	E[esz].from=u;
	E[esz].to=v;
	E[esz].next=head[u];
	head[u]=esz;
}

struct matrix {
	ll a[2][2];
	matrix(){
		a[0][0]=a[0][1]=a[1][0]=a[1][1]=-INF;
	}
	inline void set(ll x) {
		for(int i=0; i<2; i++) {
			for(int j=0; j<2; j++) a[i][j]=x;
		}
	}
	friend matrix operator * (matrix p,matrix q) {
		matrix ans;
		ans.set(-INF);
		for(int i=0; i<2; i++) {
			for(int j=0; j<2; j++) {
				for(int k=0; k<2; k++) {
					ans.a[i][j]=max(ans.a[i][j],p.a[i][k]+q.a[k][j]);
				}
			}
		}
		return ans;
	}
} mat[maxn+5];
ll val[maxn+5];
ll f[maxn+5][2],g[maxn+5][2];
ll sum=0;

struct LCT {
#define lson(x) (tree[x].ch[0])
#define rson(x) (tree[x].ch[1])
#define fa(x) (tree[x].fa)
	struct node {
		int ch[2];
		int fa;
		matrix v;
	} tree[maxn+5];
	inline bool is_root(int x) { //注意合并顺序
		return !(lson(fa(x))==x||rson(fa(x))==x);
	}
	inline int check(int x) {
		return rson(fa(x))==x;
	}
	void push_up(int x) {
		tree[x].v=mat[x];
		if(lson(x)) tree[x].v=tree[lson(x)].v*tree[x].v;
		if(rson(x)) tree[x].v=tree[x].v*tree[rson(x)].v;
	}
	void rotate(int x) {
		int y=tree[x].fa,z=tree[y].fa,k=check(x),w=tree[x].ch[k^1];
		tree[y].ch[k]=w;
		tree[w].fa=y;
		if(!is_root(y)) tree[z].ch[check(y)]=x;
		tree[x].fa=z;
		tree[x].ch[k^1]=y;
		tree[y].fa=x;
		push_up(y);
		push_up(x);
	}
	void splay(int x) {
		while(!is_root(x)) {
			int y=fa(x);
			if(!is_root(y)) {
				if(check(x)==check(y)) rotate(y);
				else rotate(x);
			}
			rotate(x);
		}
	}
	void access(int x) {
		//access的时候可能由实变虚,或由虚变实,因此要更新f,g,方法类似LCT维护虚子树信息
		//这里和树剖向上跳重链更新是类似的
		if(x==2){
			x=2;
		}
		for(int y=0; x; y=x,x=fa(x)) {
			splay(x);
			//原来的rson(x)由实变虚
			if(rson(x)){
				mat[x].a[0][0]+=max(tree[rson(x)].v.a[0][0],tree[rson(x)].v.a[1][0]);//这里也可以不用f和g,直接写对应矩阵里的值 
				mat[x].a[1][0]+=tree[rson(x)].v.a[0][0];
			} 
			rson(x)=y;
			if(rson(x)){
				mat[x].a[0][0]-=max(tree[rson(x)].v.a[0][0],tree[rson(x)].v.a[1][0]);
				mat[x].a[1][0]-=tree[rson(x)].v.a[0][0];
			}
			mat[x].a[0][1]=mat[x].a[0][0];
			push_up(x);
		}
	}
	void change(int x,ll v) {
		access(x);
		splay(x);
		mat[x].a[1][0]+=v-val[x];
		sum+=v-val[x];
		push_up(x);
		val[x]=v;
	}
	ll query(int x) {
		splay(1);//查询前记得splay到根 
		return max(tree[1].v.a[0][0],tree[1].v.a[1][0]);
	}
} T;

void dfs(int x,int fa) {
	f[x][0]=0;
	f[x][1]=val[x];
	for(int i=head[x]; i; i=E[i].next) {
		int y=E[i].to;
		if(y!=fa) {
			dfs(y,x);
			f[x][0]+=max(f[y][0],f[y][1]);
			f[x][1]+=f[y][0];
		}
	}
	mat[x].a[0][0]=mat[x].a[0][1]=f[x][0];//一开始全是轻边,f=g 
	mat[x].a[1][0]=f[x][1];
	mat[x].a[1][1]=-INF;
	T.tree[x].v=mat[x];//初始化LCT
	T.tree[x].fa=fa; //记得初始化fa
}


ll query(int x,int tx,int y,int ty) {
	ll delta=0;
	ll tmpx=val[x],tmpy=val[y];
	if(tx==1) {
		delta+=val[x]+MAXX;
		T.change(x,-MAXX);
	} else {
		T.change(x,MAXX);
	}
	if(ty==1) {
		delta+=val[y]+MAXX;
		T.change(y,-MAXX);
	} else {
		T.change(y,MAXX);
	}
	ll maxuni=T.query(1);
	ll ans=sum-maxuni+delta;
	if(ans>=MAXX) ans=-1;
	T.change(x,tmpx);
	T.change(y,tmpy);
	return ans;
}

int main() {
	int u,v,tu,tv;
	char nouse[5];
	qread(n);
	qread(m);
	scanf("%s",nouse);
	for(int i=1; i<=n; i++) {
		qread(val[i]);
		sum+=val[i];
	}
	for(int i=1; i<n; i++) {
		qread(u);
		qread(v);
		add_edge(u,v);
		add_edge(v,u);
	}
	dfs(1,0);
	for(int i=1; i<=m; i++) {
		qread(u);
		qread(tu);
		qread(v);
		qread(tv);
		qprint(query(u,tu,v,tv));
		putchar('\n');
	}
}

猜你喜欢

转载自www.cnblogs.com/birchtree/p/12663224.html