zoukankan      html  css  js  c++  java
  • 【BZOJ2286】【SDOI2011】—消耗战(虚树)

    传送门

    虚树练手题


    有一个显然的O(mn)O(mn)dpdp:

    我们考虑对于每一个点处理出两个东西

    f[i]:f[i]:表示点ii到根节点中最小的边

    g[i]:g[i]:表示将ii的子树中关键点全部切断的最小花费,如果子树没有关键点则为0

    显然每个点的答案就是min(f[i],g[i])min(f[i],g[i])

    一次dpO(n)dpO(n)


    接下来考虑如何优化

    我们可以发现每次边权是不会变的

    也就是说ff是不会变的

    变的只是必须要切断的点

    考虑到Σki<=500000Sigma k_i<=500000

    我们把每次会对答案造成影响的点单独拿出来dpdp

    那么现在的问题就是怎么样找到会对答案造成影响的点

    做法摘自自为风月马前卒

    考虑得到了询问点,如何构造出一棵虚树。

    首先我们要先对整棵树dfsdfs一遍,求出他们的dfsdfs序,然后对每个节点以dfsdfs序为关键字从小到大排序

    同时维护一个栈,表示从根到栈顶元素这条链

    假设当前要加入的节点为pp,栈顶元素为x=s[top]x=s[top]lcalca为他们的最近公共祖先
    因为我们是按照dfsdfs序遍历,因此lcalca不可能是pp

    那么现在会有两种情况:

    lcalcaxx,直接将pp入栈。

    x,px,p分别位于lcalca的两棵子树中,此时xx这棵子树已经遍历完毕,(如果没有,即xx的子树中还有一个未加入的点yy,但是dfn[y]<dfn[p]dfn[y]<dfn[p],即应先访问yy), 我们需要对其进行构建
    设栈顶元素为xx,第二个元素为yy

    dfn[y]>dfn[lca]dfn[y]>dfn[lca]可以连边y>xy>x,将xx出栈;

    dfn[y]=dfn[lca]dfn[y]=dfn[lca]y=lcay=lca连边lca>xlca>x,此时子树构建完毕(break)(break)

    dfn[y]<dfn[lca]dfn[y]<dfn[lca]lcalcay,xy,x之间,连边lca>xlca>xxx出栈,再将lcalca入栈。此时子树构建完毕(break)(break)

    #include<bits/stdc++.h>
    using namespace std;
    inline int read(){
    	char ch=getchar();
    	int res=0,f=1;
    	while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
    	while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
    	return res*f;
    }
    #define int long long
    const int N=250005;
    int n,m,adj[N],nxt[N<<1],cnt,to[N<<1];
    int f[N],a[N],val[N<<1];
    int fa[N],dep[N],siz[N],son[N],topf[N],tot,dfn[N],stk[N],top;
    vector<int> e[N];
    inline void addedge(int u,int v,int w){
    	nxt[++cnt]=adj[u],adj[u]=cnt,to[cnt]=v,val[cnt]=w;
    }
    inline void add(int u,int v){
    	e[u].push_back(v);
    }
    void dfs1(int u){
    	siz[u]=1;
    	for(int e=adj[u];e;e=nxt[e]){
    		int v=to[e];
    		if(v==fa[u])continue;
    		dep[v]=dep[u]+1,fa[v]=u;
    		f[v]=min(f[u],val[e]);
    		dfs1(v),siz[u]+=siz[v];
    		if(siz[v]>siz[son[u]])son[u]=v;
    	}
    }
    void dfs2(int u,int tp){
    	topf[u]=tp,dfn[u]=++tot;
    	if(!son[u])return;
    	dfs2(son[u],tp);
    	for(int e=adj[u];e;e=nxt[e]){
    		int v=to[e];
    		if(v==son[u]|v==fa[u])continue;
    		dfs2(v,v);
    	}
    }
    inline int Lca(int u,int v){
    	while(topf[u]!=topf[v]){
    		if(dep[topf[u]]<dep[topf[v]])v=fa[topf[v]];
    		else u=fa[topf[u]];
    	}
    	if(dep[u]<dep[v])swap(u,v);
    	return v;
    }
    inline void insert(int u){
    	if(top==1){stk[++top]=u;return;}
    	int lca=Lca(u,stk[top]);
    	if(lca==stk[top])return;
    	while(top>1&&dfn[stk[top-1]]>=dfn[lca])add(stk[top-1],stk[top]),--top;
    	if(lca!=stk[top])add(lca,stk[top]),stk[top]=lca;
    	stk[++top]=u;
    }
    inline bool comp(int a,int b){
    	return dfn[a]<dfn[b];
    }
    inline int dp(int u){
    	if(e[u].size()==0)return f[u];
    	int res=0;
    	for(int i=0;i<e[u].size();i++)
    		res+=dp(e[u][i]);
    	e[u].clear();
    	return min(res,f[u]);
    }
    signed main(){
    	f[1]=1ll<<50;
    	n=read();
    	for(int i=1;i<n;i++){
    		int u=read(),v=read(),w=read();
    		addedge(u,v,w),addedge(v,u,w);
    	}
    	dfs1(1),dfs2(1,1);
    	m=read();
    	for(int i=1;i<=m;i++){
    		int p=read();
    		for(int i=1;i<=p;i++)a[i]=read();
    		sort(a+1,a+p+1,comp);
    		stk[top=1]=1;
    		for(int i=1;i<=p;i++)insert(a[i]);
    		 while(top > 0)  add(stk[top - 1], stk[top]), top--;
    		cout<<dp(1)<<'
    ';
    	}
    }
    
  • 相关阅读:
    基础总结深入:数据类型的分类和判断(数据、内存、变量) 对象 函数 回调函数 IIFE 函数中的this 分号
    BOM 定时器 通过修改元素的类来改变css JSON
    事件 事件的冒泡 事件的委派 事件的绑定 事件的传播
    DOM修改 使用DOM操作CSS
    包装类 Date Math 字符串的相关的方法 正则表达式 DOM DOM查询
    数组 call()、apply()、bind()的使用 this arguments
    autocad 二次开发 最小包围圆算法
    win10 objectarx向导在 vs2015中不起作用的解决办法
    AutoCad 二次开发 jig操作之标注跟随线移动
    AutoCad 二次开发 文字镜像
  • 原文地址:https://www.cnblogs.com/stargazer-cyk/p/10366334.html
Copyright © 2011-2022 走看看