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


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

    题面

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

    分析

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

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

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

    因为树剖和LCT两种实现动态DP的方式常数过大,没有O2的情况下会TLE,而众所周知NOIP是没有O2优化的。因此这里只给出全局平衡二叉树写法的代码。另外两种做法的代码可以从[LuoguP4719][模板]动态DP稍加修改得到。

    代码

    全局平衡二叉树

    #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;
    	}
    	friend matrix operator * (matrix p,matrix q) {
    		matrix ans;
    		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;
    	}
    	ll* operator [](int i) {
    		return a[i];
    	}
    } mat[maxn+5];
    ll val[maxn+5];
    ll f[maxn+5][2],g[maxn+5][2];
    int sz[maxn+5],lsz[maxn+5],son[maxn+5];
    
    void dfs1(int x,int fa) {
    	sz[x]=lsz[x]=1;
    	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) {
    			dfs1(y,x);
    			f[x][0]+=max(f[y][0],f[y][1]);
    			f[x][1]+=f[y][0];
    			sz[x]+=sz[y];
    			if(sz[son[x]]<sz[y]) son[x]=y;
    		}
    	}
    	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&&y!=son[x]) {
    			g[x][0]+=max(f[y][0],f[y][1]);
    			g[x][1]+=f[y][0];
    			lsz[x]+=sz[y];
    		}
    	}
    	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;
    }
    
    ll sum=0;
    struct BST {
    #define fa(x) (tree[x].fa)
    #define lson(x) (tree[x].ch[0])
    #define rson(x) (tree[x].ch[1])
    	int root;
    	int tot;
    	int stk[maxn+5];//存储当前重链
    	int sumsz[maxn+5];//存储重链上点的lsz之和
    	struct node {
    		int fa;//全局平衡二叉树上的父亲
    		int ch[2];
    		matrix v;
    	} tree[maxn+5];
    	inline bool is_root(int x) { //注意合并顺序
    		return !(lson(fa(x))==x||rson(fa(x))==x);
    	}
    	void push_up(int x) {//很多函数和LCT是一样的
    		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;
    	}
    
    	int get_bst(int l,int r) {
    		if(l>r) return 0;
    		int mid=lower_bound(sumsz+l,sumsz+r+1,(sumsz[l-1]+sumsz[r])/2)-sumsz;//求带权重心
    		int x=stk[mid];
    		lson(x)=get_bst(l,mid-1);
    		rson(x)=get_bst(mid+1,r);//递归建树,这样的二叉树是平衡的
    		if(lson(x)) fa(lson(x))=x;//类似LCT,初始化fa和son 
    		if(rson(x)) fa(rson(x))=x;
    		push_up(x);
    		return x;
    	}
    	int build(int x,int f) {
    		int rt=0;
    		stk[++tot]=x;
    		sumsz[tot]+=lsz[x];
    		if(son[x]) { //继续dfs重链
    			sumsz[tot+1]+=sumsz[tot];
    			rt=build(son[x],x);
    		} else { //到了重链底部,可以建二叉树了
    			rt=get_bst(1,tot);
    			for(int i=1; i<=tot; i++) sumsz[i]=0;
    			tot=0;
    			return rt;
    		}
    		for(int i=head[x]; i; i=E[i].next) {
    			int y=E[i].to;
    			if(y!=f&&y!=son[x]) fa(build(y,x))=x;//对于轻链,递归下去建树,再用fa把它们连起来
    		}
    		return rt;
    	}
    	void update(int x) {
    		while(x) { //这一部分和树剖跳重链类似
    			int f=fa(x);
    			if(f&&is_root(x)) {//只有到了BST根的时候,说明已经处理完了整条重链,跳轻链到fa(x)更新上一条重链 
    				mat[f][0][0]-=max(tree[x].v[0][0],tree[x].v[1][0]);
    				mat[f][0][1]-=max(tree[x].v[0][0],tree[x].v[1][0]);
    				mat[f][1][0]-=tree[x].v[0][0];
    			}
    			push_up(x);
    			if(f&&is_root(x)) {
    				mat[f][0][0]+=max(tree[x].v[0][0],tree[x].v[1][0]);
    				mat[f][0][1]+=max(tree[x].v[0][0],tree[x].v[1][0]);
    				mat[f][1][0]+=tree[x].v[0][0];
    			}
    			x=fa(x);
    		}
    	}
    	void ini(){
    		dfs1(1,0);
    		root=build(1,0);
    	}
    	void change(int x,ll v) {
    		sum+=v-val[x];
    		mat[x][1][0]+=v-val[x];
    		val[x]=v;
    		update(x);
    	}
    	ll query(){
    		return max(tree[root].v[0][0],tree[root].v[1][0]);
    	}
    	void debug(){ 
    		printf("root=%d
    ",root); 
    		for(int i=1;i<=n;i++) printf("%d ",fa(i));
    		printf("
    ");
    	}
    } T;
    
    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();
    	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);
    	}
    	T.ini();
    	for(int i=1; i<=m; i++) {
    		qread(u);
    		qread(tu);
    		qread(v);
    		qread(tv);
    		qprint(query(u,tu,v,tv));
    		putchar('
    ');
    	}
    }
    
  • 相关阅读:
    每天一个JavaScript实例-铺货鼠标点击位置并将元素移动到该位置
    Max-Min Fairness带宽分配算法
    Centos Apache和tomcat集成配置,同一时候支持PHP和JAVA执行
    Linux硬件信息查询命令
    D3DXMatrixMultiply 函数
    垃圾回收GC:.Net自己主动内存管理 上(三)终结器
    使用python抓取CSDN关注人的全部公布的文章
    公司-科技:百度
    公司-科技:阿里巴巴
    公司-科技:腾讯
  • 原文地址:https://www.cnblogs.com/birchtree/p/12663224.html
Copyright © 2020-2023  润新知