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)<<'
    ';
    	}
    }
    
  • 相关阅读:
    个人网站
    物理读,逻辑读,预读
    正则表达式
    面向对象五大基本原则
    工作总结
    sql性能优化
    sqlServer游标的使用
    ASP.NET安全[开发ASP.NET MVC应用程序时值得注意的安全问题](转)
    一个简单问题引发对IEnumerable和IQueryable的思考
    EFCodeFirst 各种命令整理
  • 原文地址:https://www.cnblogs.com/stargazer-cyk/p/10366334.html
Copyright © 2011-2022 走看看