问题描述

在一场战争中,战场由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\n",dp(1,0));
}
return 0;
}
//P.S. 树链剖分找LCA跑的真快......

[洛谷P2459] SDOI2011 消耗战的更多相关文章

  1. 洛谷 P2495 [SDOI2011]消耗战(虚树,dp)

    题面 洛谷 题解 虚树+dp 关于虚树 了解一下 具体实现 inline void insert(int x) { if (top == 1) {s[++top] = x; return ;} int ...

  2. 洛谷P2495 [SDOI2011]消耗战(虚树dp)

    P2495 [SDOI2011]消耗战 题目链接 题解: 虚树\(dp\)入门题吧.虚树的核心思想其实就是每次只保留关键点,因为关键点的dfs序的相对大小顺序和原来的树中结点dfs序的相对大小顺序都是 ...

  3. ●洛谷P2495 [SDOI2011]消耗战

    题链: https://www.luogu.org/problemnew/show/P2495题解: 虚树入门,树形dp 推荐博客:http://blog.csdn.net/lych_cys/arti ...

  4. bzoj 2286(洛谷 2495) [Sdoi2011]消耗战——虚树

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2286 https://www.luogu.org/problemnew/show/P2495 ...

  5. 洛谷P2495 [SDOI2011]消耗战(虚树)

    题面 传送门 题解 为啥一直莫名其妙\(90\)分啊--重构了一下代码才\(A\)掉-- 先考虑直接\(dp\)怎么做 树形\(dp\)的时候,记一下断开某个节点的最小值,就是从根节点到它的路径上最短 ...

  6. [洛谷P2495][SDOI2011]消耗战

    题目大意:有一棵$n(n\leqslant2.5\times10^5)$个节点的带边权的树,$m$个询问,每次询问给出$k(\sum\limits_{i=1}^mk_i\leqslant5\times ...

  7. [洛谷P2491] [SDOI2011]消防

    洛谷题目链接:[SDOI2011]消防 题目描述 某个国家有n个城市,这n个城市中任意两个都连通且有唯一一条路径,每条连通两个城市的道路的长度为zi(zi<=1000). 这个国家的人对火焰有超 ...

  8. BZOJ2243 洛谷2486 [SDOI2011]染色 树链剖分

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ2243 题目传送门 - 洛谷2486 题意概括 一棵树,共n个节点. 让你支持以下两种操作,共m次操 ...

  9. 洛谷 P2486 [SDOI2011]染色/bzoj 2243: [SDOI2011]染色 解题报告

    [SDOI2011]染色 题目描述 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同 ...

随机推荐

  1. Junit单元测试的使用

    这里拿Dynamic Web Project项目来演示,首先创建一个Dynamic Web Project项目,起名,点next, 继续点next, 将web.xml文件勾选,finish, 接下来在 ...

  2. Django学习之视图

    一.Django的View(视图) 1.一个简单的视图 3.CBV和FBV 4.给视图加装饰器 使用装饰器装饰FBV 使用装饰器装饰CBV 二.Request对象和Response对象 1.reque ...

  3. scrapy Pipeline使用twisted异步实现mysql数据插入

    from twisted.enterprise import adbapi class MySQLAsyncPipeline: def open_spider(self, spider): db = ...

  4. 006-spring-data-elasticsearch 3.0.0.0使用【四】-spring-data之Elasticsearch Repositories

    续 二.Elasticsearch Repositories 2.1.简介 2.1.1.Spring命名空间 Spring Data Elasticsearch模块包含一个允许定义存储库bean的自定 ...

  5. Delphi XE2 之 FireMonkey 入门(13) - 动画(下)

    TAnimation 类的主要成员: protected   function NormalizedTime: Single;      //   procedure ProcessAnimation ...

  6. delphi自定义事件处理

    http://www.cnblogs.com/ywangzi/archive/2012/09/06/2673414.html delphi自定义事件处理   为什么我们点击按钮,就会执行按钮的oncl ...

  7. 44 答疑(三)--join的写法/Simple nested loop join的性能问题/Distinct和group by的性能/备库自增主键问题

    44 答疑(三) Join的写法 35节介绍了join执行顺序,加了straight_join,两个问题: --1 如果用left join,左边的表一定是驱动表吗 --2 如果两个表的join包含多 ...

  8. linux下安装python27 nginx 和uwsgi

    注意: python27 默认没有安装 pip 和setuptools所以要提前安装.(务必先提前安装python27 哈 ) wget --no-check-certificate https:// ...

  9. Java日志使用slf4j 配置log4j后,有日志文件 但日志文件内容为空

    SLF4J的全称是Simple Logging Facade for Java,即简单日志门面. SLF4J并不是具体的日志框架,而是作为一个简单门面服务于各类日志框架,如java.util.logg ...

  10. java Map与Bean相互转化

    import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector ...