• [bzoj2286] [洛谷P2495] [sdoi2015] 消耗战


    Description

    在一场战争中,战场由 (n) 个岛屿和 (n-1) 个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他 (k) 个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。
    侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用 (m) 次,所以我们只需要把每次任务完成即可。

    Input

    第一行一个整数 (n),代表岛屿数量。

    接下来 (n-1) 行,每行三个整数 (u,v,w),代表 (u) 号岛屿和 (v) 号岛屿由一条代价为 (c) 的桥梁直接相连,保证 (1leq u,v leq n) 且$ 1 leq c leq 100000$。

    (n+1) 行,一个整数 (m),代表敌方机器能使用的次数。

    接下来 (m) 行,每行一个整数 (k_i) ,代表第 (i) 次后,有 (k_i) 个岛屿资源丰富,接下来 (k) 个整数 $ h_1,h_2,…h_k$,表示资源丰富岛屿的编号。

    Output

    输出有 (m) 行,分别代表每次任务的最小代价。

    Sample Input

    10

    1 5 13

    1 9 6

    2 1 19

    2 4 8

    2 3 91

    5 6 8

    7 5 4

    7 8 31

    10 7 9

    3

    2 10 6

    4 5 7 8 3

    3 9 4 6

    Sample Output

    12

    32

    22

    HINT

    对于100%的数据,(2leq n leq 250000,1 leq m,sum k_i leq 500000,1 leq k_i leq n-1)


    想法

    注意到数据范围中重要的提示 $sum k_i leq 500000 $
    于是我们对于每次询问把需要用到的点(最多 (2k) 个)挑出来建成一棵新树,然后在新树上进行树形dp就行了

    说的好容易的样子…实际上“新树”就是“虚树”,它的建树过程是十分巧妙的。

    首先把询问点根据原树DFS序排序,显然这些点都要出现在虚树中来,而且为了保证结构不被破坏,另外一些跟他们有关系的点都要加入到虚树中来。我们用一个栈,维护原树上的一条链,自栈底到栈顶,深度由小变大。每次考虑插入询问点进栈。如果插入点的祖先是栈顶元素,那么直接插入即可,因为反正是一条链上的结点。如果不是的话,那么只有可能分居他们的lca的两棵子树中。现在我们就需要分类讨论,如果栈顶元素的下一位的深度比lca深,那么我们需要不断弹出栈顶元素,并且在弹出之前与栈顶下一位连一下边。直到lca深度比栈顶元素深,此时把lca加入栈,把需要插入的点加入栈,继续往下处理。又因为我们是按DFS序做的,这样就可以保证我们开始说的,维护的是树上的一条链。之后再DP就可以了。
    ——by ljh2000


    代码

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    
    #define INF 1e14
    
    typedef long long ll;
    using namespace std;
    
    const int N = 250005;
    
    struct node{
    	int v,len;
    	node *next;
    }pool[N*2],*h[N],pool2[N*2],*h2[N];
    int cnt,cnt2;
    void addedge(int u,int v,int l){
    	node *p=&pool[++cnt],*q=&pool[++cnt];
    	p->v=v;p->next=h[u];h[u]=p;p->len=l;
    	q->v=u;q->next=h[v];h[v]=q;q->len=l;
    }
    void addedge2(int u,int v,int l){
    	node *p=&pool2[++cnt2];
    	p->v=v;p->next=h2[u];h2[u]=p;p->len=l;
    }
    
    int n,m;
    int dfn[N],f[N][20],fmin[N][20],dep[N],tot;
    void dfs(int u){
    	int v;
    	dfn[u]=++tot;
    	for(node *p=h[u];p;p=p->next)
    		if(!dfn[v=p->v]){
    			f[v][0]=u; fmin[v][0]=p->len;
    			for(int j=1;j<20;j++){
    				f[v][j]=f[f[v][j-1]][j-1];
    				fmin[v][j]=min(fmin[f[v][j-1]][j-1],fmin[v][j-1]);
    			}
    			dep[v]=dep[u]+1;
    			dfs(v);
    		}
    }
    int lca(int x,int y){
    	if(dep[x]<dep[y]) swap(x,y);
    	for(int i=19;i>=0;i--)
    		if(dep[f[x][i]]>=dep[y]) x=f[x][i];
    	if(x==y) return x;
    	for(int i=19;i>=0;i--)
    		if(f[x][i]!=f[y][i])
    			x=f[x][i],y=f[y][i];
    	return f[x][0];
    }
    int mindis(int x,int y){
    	if(dep[x]<dep[y]) swap(x,y);
    	if(x==y) return 0;
    	int t=N;
    	for(int i=19;i>=0;i--)
    		if(dep[f[x][i]]>=dep[y]){
    			t=min(t,fmin[x][i]);
    			x=f[x][i];
    		}
    	return t;
    }
    
    ll w[N];
    int g[N];
    void dp(int u){
    	int v;
    	w[u]=0;
    	for(node *p=h2[u];p;p=p->next){
    		dp(v=p->v); 
    		w[u]+=min(w[v],(ll)p->len);
    	} 
    	h2[u]=NULL; //clear 保证每次邻接矩阵清空效率为O(k)
    	if(g[u]) w[u]=INF;
    }
    
    int st[N],top;
    int K,k[N];
    bool cmp(int x,int y) { return dfn[x]<dfn[y]; }
    void work(){
    	cnt2=0; //clear
    	
    	sort(k,k+K,cmp);
    	//build tree
    	top=0; st[top++]=1;
    	for(int i=0;i<K;i++){
    		int now=k[i],f=lca(now,st[top-1]);
    		while(1){
    			if(top<=1) { if(st[top-1]!=f) st[top++]=f; break; }
    			if(dep[f]>=dep[st[top-2]]){
    				if(f!=st[top-1]) addedge2(f,st[top-1],mindis(f,st[top-1]));
    				if(f==st[top-2]) top--;
    				else st[top-1]=f;
    				break;
    			}
    			addedge2(st[top-2],st[top-1],mindis(st[top-1],st[top-2]));
    			top--;
    		}
    		if(now!=st[top-1]) st[top++]=now;
    	}
    	while(top>1) {
    		addedge2(st[top-2],st[top-1],mindis(st[top-1],st[top-2]));
    		top--;
    	}
    	
    	dp(1);
    	printf("%lld
    ",w[1]);
    }
    
    int main()
    {
    	int u,v,l;
    	scanf("%d",&n);
    	for(int i=1;i<n;i++){
    		scanf("%d%d%d",&u,&v,&l);
    		addedge(u,v,l);
    	}
    	dep[1]=1; dfs(1);
    	
    	scanf("%d",&m);
    	while(m--){
    		scanf("%d",&K);
    		for(int i=0;i<K;i++) scanf("%d",&k[i]),g[k[i]]=1;
    		work();
    		for(int i=0;i<K;i++) g[k[i]]=0;
    	}
    	
    	return 0;
    }
    
    既然选择了远方,便只顾风雨兼程
  • 相关阅读:
    SpringSecurity配置
    SpringBoot整合Mybatis
    springboot整合JDBC
    SpringBoot的WEB流程
    List&TreeSet实现斗地主发牌及牌序
    SpringBoot第一个程序
    项目错误总结2021.4.21
    Spring整合Mybatis
    学习——构建民航业知识图谱并实现语义查询的
    新闻文本分类——词云可视化
  • 原文地址:https://www.cnblogs.com/lindalee/p/9074065.html
Copyright © 2020-2023  润新知