问题描述

在一场战争中,战场由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. React-Native 之 GD (十七)小时风云榜按钮处理

    小时风云榜按钮处理 在服务器返回给我们的 json 数据中,提供了 hasnexthour 字段,当这个字段返回为 1 的时候,表示后面还有内容,按钮可以点击,否则不能点击,按照这个思路,我们就来完成 ...

  2. 单页应用 cookies处理

    w Node & 单页应用 来做一个完整用户系统吧! - harryfyodor的前端专栏 - SegmentFaulthttps://segmentfault.com/a/119000000 ...

  3. All strings must be XML compatible: Unicode or ASCII, no NULL bytes or control characters

    ValueError: All strings must be XML compatible: Unicode or ASCII, no NULL bytes or control character ...

  4. python监控ip攻击,服务器防火墙

    '''写一个程序,监控nginx的日志,如果有人攻击就加入黑名单 把ip加入黑名单的策略是,1分钟之内,如果同一个ip请求超过200次,那就加入黑名单''' '''分析:1.打开文件 2.循环读取 3 ...

  5. ASP.NET Core 上传微信永久视频素材

    话不多说直接上源码 请求实体  public class AddVideoRequest    {        /// <summary>        /// 文件流        / ...

  6. 类Vector

    /* * Vector的特有功能 * * Vector出现较早,比集合更早出现 * * 1:添加功能 * public void addElement(Object obj);//用add()替代 * ...

  7. [Web 前端] 019 css 定位之绝对定位与相对定位

    1. 关于定位 我们可以使用 css 的 position 属性来设置元素的定位类型 postion 的设置项如下 设置项 释义 relative 生成相对定位元素元素所占据的文档流的位置不变元素本身 ...

  8. MySQL-第十三篇使用ResultSetMetaData分析结果集

    1.Result里面包含了一个getMetaData()方法,该方法返回该ResultSet对应的ResultSetMetaData对象. 2.ResultSetMetaData包含的方法: 1> ...

  9. ubuntu开机只有一条横杠在闪的解决办法

    1.制作U盘启动盘,并试用ubuntu 2.输入以下命令,根据提示完成修复 sudo add-apt-repository ppa:yannubuntu/boot-repair && ...

  10. py基础

    基本语句和函数等练习,知识点都在代码里... """ a = int(input('a = ')) b = int(input('b = ')) print('%d + ...