zoukankan      html  css  js  c++  java
  • 【虚树学习笔记([SDOI2011]消耗战)】

    题意

    [SDOI2011]消耗战

    想法

    首先我们可以很自然的想到怎么在整棵树上进行求解\(DP\)
    很简单 每个点有两个选择 要么对其子树的关键点递归求解 要么自己断开

    当然断开的\(cost\)为其到根的最短边权

    但我们发现每次都进行一次\(O(n)\)的整棵树\(DP\)我们实在是不太敢用

    于是这个时候虚树起到了他的作用

    给出定义:虚树是一些关键点按照在原树的祖孙关系构建的树
    \(当A在原树中是B的祖先,那么在虚树中任然是\)
    同时虚树中不仅有关键点,为了保持其原有的一些信息,一些关键点的\(LCA\)仍是必要存在的

    芝士:虚树

    我们怎么构建这个虚树呢,我们首先对每个点标号\(dfn\),然后对关键点按标号排序

    这样保证我们是一条一条链加进来的

    我们逐个加入

    1.如果栈为空,或者栈中只有一个元素,那么显然应该:
    \(stk[++top]=u;\)
    2.取\(lca=LCA(u,stk[top])\),如果\(lca=stk[top]\)则说明\(u\)点应该接着\(stk[top]\)点延长当前的树链.做操作:
    \(stk[++top]=u;stk[++top]=u;\)
    3.如果\(lca≠stk[top]\)则说明\(u与stk[top]\)分属\(lca\)的两颗不同的子树,且包含\(stk[top]\)的这颗子树应该已经构建完成了,我们需要做的是:
    \(lca\)的包含\(stk[top]\)子树的那部分退栈,并将这部分建边形成虚树.如果\(lca\)不在栈(树链)中,那么要把\(lca\)也加入栈中,保证虚树的结构不出现问题,随后将u加入栈中,以表延长树链.

    这里建议画图思考

    建完虚树在虚树上跑就行了

    代码

    #include<iostream>
    #include<cstdio>
    #include<vector>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    
    ll n,k;
    
    struct P{
    	int to,next,v;
    };
    
    int dfn[50005],mn[50005];
    ll cnt = 0;
    
    struct T{
    	int head[25010];
    	P e[500010];
    	void add(ll x,ll y,ll v){
    		e[++cnt].to = y;
    		e[cnt].next = head[x];
    		head[x] = cnt;
    		e[cnt].v = v;
    	} 
    	int d[25010],fa[25010],son[25010],s[25010];                                                                             
    	void dfs(ll now,ll f){
    		d[now] = d[f] + 1;
    		fa[now] = f;
    		ll maxx = 0;
    		s[now] = 1;
    		for(int i = head[now];i;i = e[i].next){
    			if(e[i].to == f)continue;
    			mn[e[i].to] = std::min(mn[now],e[i].v);
    			dfs(e[i].to,now);
    			s[now] += s[e[i].to];
    			if(s[e[i].to] > maxx)
    			maxx = s[e[i].to],son[now] = e[i].to; 
    		}
    	}
    	int dfncnt,top[250010];
    	void dfs2(ll now,ll tp){
    		dfn[now] = ++dfncnt;
    		top[now] = tp;
    		if(!son[now])return ;
    		dfs2(son[now],tp);
    		for(int i = head[now];i;i = e[i].next){
    			if(e[i].to == fa[now] || e[i].to == son[now])continue;
    			dfs2(e[i].to,e[i].to); 
    		}
    	}
    	ll lca(ll x,ll y){
    		while(top[x]!=top[y]){
    			if(d[top[x]]<d[top[y]]) x^=y^=x^=y;
    			x=fa[top[x]];
    		}
    		return d[x]>d[y]?y:x;
    	} 
    }Q;
    
    ll t;
    ll top = 0;
    int s[25010];
    	
    struct FT{
    	std::vector<int>son[25010];
    	void add(ll x,ll y){son[x].push_back(y);}
    	void ins(ll x){
    		if(top <= 1) {s[++top] = x;return ;}
    		ll l = Q.lca(x,s[top]);
    		if(l == s[top]){return ;}
    		while(top > 1 && dfn[s[top - 1]] >= dfn[l]){
    			add(s[top - 1],s[top]);
    			--top;
    		}
    		if(l != s[top]) add(l,s[top]),s[top] = l;
    		s[++top] = x;
    	}
    	ll get(ll now){
    		if(son[now].size() == 0) return mn[now];
    		ll ans = 0;
    		for(register int i = 0;i < son[now].size();++i)
    		ans += get(son[now][i]);
    		son[now].clear();
    		return std::min(ans,(ll)mn[now]);
    	}	
    }W;
    
    bool cmp(ll x,ll y){return dfn[x] < dfn[y];}
    
    int num[250010];
    
    int main(){
    	memset(mn,0x3f,sizeof(mn));
    	scanf("%lld",&n);
    	for(int i = 1;i <= n - 1;++i){
    		ll x,y,v;
    		scanf("%lld%lld%lld",&x,&y,&v);
    		Q.add(x,y,v);
    		Q.add(y,x,v);
    	}
    	Q.dfs(1,0);
    	Q.dfs2(1,1);
    	scanf("%lld",&t);
        while(t -- ){
        	ll k;
        	scanf("%lld",&k);
        	for(int i = 1;i <= k;++i)
        	scanf("%d",&num[i]);
    		std::sort(num + 1,num + k + 1,cmp);
    		s[top = 1] = 1;
    		for(int i = 1;i <= k;++i)
    		W.ins(num[i]);
    		while(top > 0)W.add(s[top - 1],s[top]),top -- ;
    		std::cout<<W.get(1)<<std::endl;
    	}
    }
    
  • 相关阅读:
    C# 反射修改私有静态成员变量
    symfony2 controller
    symfony2 路由工作原理及配置
    symfony2 安装并创建第一个页面
    git操作命令
    Node异步I/O、事件驱动与高性能服务器
    nodejs get/request
    nodejs events模块
    nodejs 小爬虫
    nodejs API
  • 原文地址:https://www.cnblogs.com/dixiao/p/14379881.html
Copyright © 2011-2022 走看看