zoukankan      html  css  js  c++  java
  • [洛谷P2459] SDOI2011 消耗战

    问题描述

    在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。

    侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用m次,所以我们只需要把每次任务完成即可。

    输入格式

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

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

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

    接下来m行,每行一个整数ki,代表第i次后,有ki个岛屿资源丰富,接下来k个整数h1,h2,…hk,表示资源丰富岛屿的编号。

    输出格式

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

    样例输入

    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

    样例输出

    12
    32
    22

    数据范围

    对于100%的数据,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1

    解析

    一道虚树板子题......首先,来考虑如何用动态规划来做。为了删除最小的边使1号点与任意一个能源点不连通,显然,对于一个点,要么使它的子树中的所有点不与该点连接,要么使该点不与父节点相连。所以一个节点(u)节点产生的最小代价为(min(sum,w(u,v)))(其中(sum)表示第一种方案的代价,(v)表示(u)的父节点)。特殊情况是该节点就是一个能源节点,那么对于这棵子树的最小代价就是第二种方案的代价。

    既然动态规划方案想好了,下面用虚树进行优化。将每一个能源点以及它们的LCA作为关键点,重新建立一棵树,结构与原树相似,但只保留了关键点。这棵新树就叫做虚树。由于要求最小值,那么虚树上的边权原树上两点间路径上的最小边权。但这样做还要求两点间路径,时间直接爆炸,有没有办法优化呢?

    考虑到虚树上两相邻节点(u,v)以及(u)的父节点(f),在原树上(f)(v)的路径必然会经过(u),那么如果(f)(u)的最小值小于(u)(v)的最小值,显然断掉(w(u,f))更优,反之则断掉(w(u,v))更优。由此,可以发现答案必然是(v)(f)上的最小值。广而推之,一个点的父边权可以直接赋值为它到根节点上路径的最小值,不影响答案。

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <algorithm>
    #define N 500002
    using namespace std;
    int head[N],ver[2*N],nxt[2*N],l;
    int n,m,i,j,d[N],size[N],son[N],fa[N],top[N],dfn[N],k,t,cnt,s[2*N],h[2*N];
    long long dis[N],edge[N*2];
    bool e[N];
    int read()
    {
    	char c=getchar();
    	int w=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c<='9'&&c>='0'){
    		w=w*10+c-'0';
    		c=getchar();
    	}
    	return w;
    }
    void insert(int x,int y,long long z)
    {
    	l++;
    	ver[l]=y;
    	edge[l]=z;
    	nxt[l]=head[x];
    	head[x]=l;
    }
    void dfs1(int x,int pre,int dep)
    {
    	fa[x]=pre;size[x]=1;
    	dfn[x]=++cnt;d[x]=dep;
    	for(int i=head[x];i;i=nxt[i]){
    		int y=ver[i];
    		if(y!=pre){
    			dis[y]=min(dis[x],edge[i]);
    			dfs1(y,x,dep+1);
    			size[x]+=size[y];
    			if(size[y]>size[son[x]]) son[x]=y;
    		}
    	}
    }
    void dfs2(int x,int y)
    {
    	top[x]=y;
    	if(son[x]) dfs2(son[x],y);
    	for(int i=head[x];i;i=nxt[i]){
    		int y=ver[i];
    		if(!top[y]) dfs2(y,y);
    	}
    }
    int LCA(int x,int y)
    {
    	if(x==0||y==0) return 0;
    	while(top[x]!=top[y]){
    		if(d[top[x]]<d[top[y]]) swap(x,y);
    		x=fa[top[x]];
    	}
    	if(d[x]<d[y]) swap(x,y);
    	return y;
    }
    int cmp(const int &x,const int &y)
    {
    	return dfn[x]<dfn[y];
    }
    long long dp(int u,int pre)
    {
        if(e[u]) return dis[u]; 
        long long sum = 0;
        for(int i = head[u]; i; i = nxt[i]) if(ver[i]!=pre) sum += dp(ver[i],u); 
        return min(sum, dis[u]); 
    }
    int main()
    {
    	cin>>n;
    	for(i=1;i<n;i++){
    		int u,v,w;
    		u=read();v=read();w=read();
    		insert(u,v,w);insert(v,u,w);
    	}
    	dis[1]=1LL<<50;
    	dfs1(1,0,0);
    	dfs2(1,0);
    	cin>>m;
    	for(i=1;i<=m;i++){
    		memset(e,0,sizeof(bool)*(n+5));
    		memset(head,0,sizeof(int)*(n+5));
    		l=t=0;
    		k=read();
    		for(j=1;j<=k;j++) h[j]=read(),e[h[j]]=1;
    		sort(h+1,h+k+1,cmp);
    		for(j=1;j<k;j++) h[j+k]=LCA(h[j],h[j+1]);
    		k*=2;h[k]=1;
    		sort(h+1,h+k+1,cmp);
    		k=unique(h+1,h+k+1)-h-1;
    		for(j=1;j<=k;j++){
    			while(t&&dfn[s[t]]+size[s[t]]-1<dfn[h[j]]) t--;
    			if(t){
    				insert(s[t],h[j],dis[h[j]]);
    				insert(h[j],s[t],dis[h[j]]);
    			}
    			s[++t]=h[j];
    		}
    		printf("%lld
    ",dp(1,0));
    	}
    	return 0;
    }
    //P.S. 树链剖分找LCA跑的真快......
    
  • 相关阅读:
    聊天界面的实现
    继续,迫不及待想学数据库
    今天休息,我来研究上次的代码了
    项目导入
    命令行常用的一下命令
    svn的安装和基本操作,及常见问题
    maven项目里写测试
    Eclipse里新建maven项目
    maven简介
    重装win7,没有管理员权限,没有以管理员身份运行
  • 原文地址:https://www.cnblogs.com/LSlzf/p/10987562.html
Copyright © 2011-2022 走看看