Description

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

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

Input

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

接下来 \(n-1\) 行,每行三个整数 \(u,v,w\),代表 \(u\) 号岛屿和 \(v\) 号岛屿由一条代价为 \(c\) 的桥梁直接相连,保证 \(1\leq 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%的数据,\(2\leq 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\n",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;
}

[bzoj2286] [洛谷P2495] [sdoi2015] 消耗战的更多相关文章

  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. 洛谷P2495 [SDOI2011]消耗战(虚树)

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

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

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

  6. 洛谷P3324 [SDOI2015]星际战争

    题目:洛谷P3324 [SDOI2015]星际战争 思路: 类似<导弹防御塔>,因为题目保证有解,花费时间小于最终答案时一定无法消灭所有敌人,只要花费时间大于等于最终答案都可以消灭所有敌人 ...

  7. LOJ #2185 / 洛谷 P3329 - [SDOI2015]约数个数和(莫比乌斯函数)

    LOJ 题面传送门 / 洛谷题面传送门 题意: 求 \(\sum\limits_{i=1}^n\sum\limits_{j=1}^md(ij)\),\(d(x)\) 为 \(x\) 的约数个数. \( ...

  8. AC日记——[SDOI2011]消耗战 洛谷 P2495

    [SDOI2011]消耗战 思路: 建虚树走树形dp: 代码: #include <bits/stdc++.h> using namespace std; #define INF 1e17 ...

  9. [虚树模板] 洛谷P2495 消耗战

    题意:给定树上k个点,求切断这些点到根路径的最小代价.∑k <= 5e5 解:虚树. 构建虚树大概是这样的:设加入点与栈顶的lca为y,比较y和栈中第二个元素的DFS序大小关系. 代码如下: i ...

随机推荐

  1. 51nod 1287加农炮

    1287 加农炮  题目来源: Codility 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题 一个长度为M的正整数数组A,表示从左向右的地形高度.测试一种加农炮 ...

  2. 2018百度之星资格赛A B F

    A.调查问卷 度度熊为了完成毕业论文,需要收集一些数据来支撑他的论据,于是设计了一份包含 mm 个问题的调查问卷,每个问题只有 'A' 和 'B' 两种选项. 将问卷散发出去之后,度度熊收到了 nn  ...

  3. ZR1158

    ZR1158 http://www.zhengruioi.com/contest/446/problem/1158 给定限制的问题大多数都是容斥或者二分,或者二分之后容斥 首先,这个问题的第一步我们还 ...

  4. vue-learning:29 - component - 组件三大API之三:slot

    组件三大API之三: slot <slot>标签 v-slot指令 普通插槽 有默认值的插槽 具名插槽 作用域插槽 v-slot是Vue 2.6.0引入的一个新语法指令,目的是统一之前sl ...

  5. 2018-8-10-C#-6.0-字符串-String-Interpolation

    title author date CreateTime categories C# 6.0 字符串 String Interpolation lindexi 2018-08-10 19:16:52 ...

  6. 解决IDEA使用terminal时 git log 乱码

    1.配置环境变量:  变量名称-LESSCHARSET 变量值:utf-8 2.重启IDEA

  7. 在Linux中执行.sh脚本,异常/bin/sh^M

    在Linux中执行.sh脚本,异常/bin/sh^M 在linux中执行.sh脚本,异常/bin/sh^M: bad interpreter: No such file or directory. 分 ...

  8. JAVA8学习——深入Comparator&Collector(学习过程)

    深入Comparator&Collector 从源码深入Comparator Comparator从Java1.2就出来了,但是在1.8的时候,又添加了大量的默认方法. compare() e ...

  9. 基于Tesseract的OCR识别小程序

    一.背景 先说下开发背景,今年有次搬家找房子(2020了应该叫去年了),发现每天都要对着各种租房广告打很多电话.(当然网上也找了实地也找),每次基本都是对着墙面看电话号码然后拨打,次数一多就感觉非常麻 ...

  10. Java网络编程——TCP图片上传

    1.编写一个服务器端程序,用来接收图片.创建一个监听指定端口号的ServerSocket服务端对象,在while(true)无限循环中持续调用ServerSocket的accept()方法来接收客户端 ...