[Bzoj2286][Sdoi2011]消耗战(虚树模板题附讲解)
2286: [Sdoi2011]消耗战
Time Limit: 20 Sec Memory Limit: 512 MB
Submit: 4896 Solved: 1824
[Submit][Status][Discuss]
Description
Input
第一行一个整数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,表示资源丰富岛屿的编号。
Output
输出有m行,分别代表每次任务的最小代价。
Sample Input
Sample Output
HINT
对于100%的数据,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1
Source
设红色点为关键点,让我们来看看最后的虚树
橙色点是虚树中关键点的lca,虚点和虚边就是被去掉了。
这里看起来是两条边,其实合成了一条边
最后输出虚树根节点(root)即可。
然后我们还发现一个性质就是一个点是关键点,那么它的儿子结点可以直接忽略。
那么说了这道题怎么做了,就差怎么建出虚树了。
建虚树首先我们dfs一遍原树,建出dfs序。
我们先把关键点按从小到大排序。
然后用一个栈的形式维护一条链。
简单说
如果当前点和栈顶为一条链,那么可以直接加入栈。
如果不为一条链,那么得一直删除栈顶,直到形成一条链。
怎么具体实现呢?
设当前点和栈顶lca为g
那么如果dfn[g] >= dfn[top]那么很显然是一条链,直接push当前点即可
如果dfn[g] < dfn[top]那么不是一条链,边pop,边连边栈顶第二个个元素和栈顶。
知道dfn[g] >= dfn[top - 1],那么把top - 1 和 top连边后加入g即可。
最后一条链没有点去pop它,所以最后还要特殊判断栈内是否还要有一条链。
这样就很方便了。
AC代码:
# include <iostream>
# include <cstdio>
# include <cstring>
# include <algorithm>
using namespace std;
typedef long long LL;
const int N = 6e5 + ;
const LL inf = 1e15;
int hson[N],sz[N],dep[N],top[N],id[N],fa[N];LL f[N];
int head[N],dt,tot,que[N],n,m,k,a[N],cnt;LL w[N];
struct Edge{
int to,nex;LL w;
}edge[N << ];
void AddEdge(int u,int v,LL w)
{
if(u == v)return;
edge[++dt] = (Edge){v,head[u],w};
head[u] = dt;
}
void dfs(int u)
{
sz[u] = ;
for(int i = head[u];i;i = edge[i].nex)
{
if(sz[edge[i].to])continue;
dep[edge[i].to] = dep[u] + ;
fa[edge[i].to] = u;
w[edge[i].to] = min(w[u],edge[i].w);
dfs(edge[i].to);
sz[u] += sz[edge[i].to];
if(sz[hson[u]] < sz[edge[i].to])hson[u] = edge[i].to;
}
}
void dfs(int u,int tp)
{
top[u] = tp;id[u] = ++tot;
if(hson[u])dfs(hson[u],tp);
for(int i = head[u];i;i = edge[i].nex)
if(!id[edge[i].to])dfs(edge[i].to,edge[i].to);
head[u] = ;
}
int lca(int u,int v)
{
while(top[u] != top[v])
{
if(dep[top[u]] < dep[top[v]])swap(u,v);
u = fa[top[u]];
}
return dep[u] < dep[v] ? u : v;
}
bool cmp(int x,int y){return id[x] < id[y];}
void dp(int u)
{
LL val = ;f[u] = w[u];
for(int i = head[u];i;i = edge[i].nex)
dp(edge[i].to),val += f[edge[i].to];
if(val)f[u] = val < f[u] ? val : f[u];
head[u] = ;
}
void solve()
{ scanf("%d",&k);cnt = ;int g,top;dt = ;
for(int i = ;i <= k;i++)scanf("%d",&a[i]);
sort(a + ,a + k + ,cmp);
for(int i = ;i <= k;i++)if(lca(a[i],a[cnt]) != a[cnt])a[++cnt] = a[i];
que[top = ] = ;
for(int i = ;i <= cnt;i++)
{
g = lca(a[i],que[top]);
while()
{
if(dep[que[top - ]] <= dep[g])
{
AddEdge(g,que[top],);top--;
if(que[top] != g)que[++top] = g;
break;
}
AddEdge(que[top - ],que[top],);top--;
}
if(que[top] != a[i])que[++top] = a[i];
}
top--;
while(top)AddEdge(que[top],que[top + ],),top--;
dp();
printf("%lld\n",f[]);
}
int main()
{
scanf("%d",&n);int x,y;LL z;w[] = inf;
for(int i = ;i < n;i++)
{
scanf("%d %d %lld",&x,&y,&z);
AddEdge(x,y,z);AddEdge(y,x,z);
}
dfs();dfs(,);
scanf("%d",&m);
while(m--)solve();
}
[Bzoj2286][Sdoi2011]消耗战(虚树模板题附讲解)的更多相关文章
- [BZOJ2286][SDOI2011]消耗战(虚树DP)
2286: [Sdoi2011]消耗战 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 4998 Solved: 1867[Submit][Statu ...
- bzoj2286: [Sdoi2011]消耗战 虚树
在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个 ...
- BZOJ2286: [Sdoi2011]消耗战(虚树/树形DP)
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 5246 Solved: 1978[Submit][Status][Discuss] Descript ...
- BZOJ 2286 [Sdoi2011]消耗战 ——虚树
虚树第一题. 大概就是建一颗只与询问有关的更小的新树,然后在虚树上DP #include <map> #include <ctime> #include <cmath&g ...
- 【BZOJ2286】[Sdoi2011]消耗战 虚树
[BZOJ2286][Sdoi2011]消耗战 Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的 ...
- 【BZOJ-2286】消耗战 虚树 + 树形DP
2286: [Sdoi2011消耗战 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 2120 Solved: 752[Submit][Status] ...
- bzoj 2286: [Sdoi2011]消耗战 虚树+树dp
2286: [Sdoi2011]消耗战 Time Limit: 20 Sec Memory Limit: 512 MB[Submit][Status][Discuss] Description 在一 ...
- [SDOI2011]消耗战(虚树+树形动规)
虚树dp 虚树的主要思想: 不遍历没用的的节点以及没用的子树,从而使复杂度降低到\(\sum\limits k\)(k为询问的节点的总数). 所以怎么办: 只把询问节点和其LCA放入询问的数组中. 1 ...
- P2495 [SDOI2011]消耗战 虚树
这是我做的第一道虚树题啊,赶脚不错.其实虚树也没什么奇怪的,就是每棵树给你一些点,让你多次查询,但是我不想每次都O(n),所以我们每次针对给的点建一棵虚树,只包含这些点和lca,然后在这棵虚树上进行树 ...
随机推荐
- ubuntu下安装redis扩展
采用源码编译安装 下载 下载redis源码包到本地一个临时目录 git clone https://github.com/phpredis/phpredis.git 移动文件到合适目录 mv phpr ...
- EF为什么向我的数据库再次插入已有对象?(ZT)
最近做了个多对多对实体对象,结果发现每次只要增加一个子实体,就会自动添加一个父实体进去,而不管该父实体是否已经存在. 找了好久,终于找到这篇文章,照文章内容来看,应该是断开连接导致的. 原文地址:ht ...
- vscode 用户代码片段 vue初始化模板 Snippet #新加入开头注释 自动生成文件名 开发日期时间等内容
vue文件模板 模板变量 https://code.visualstudio.com/docs/editor/userdefinedsnippets#_variables vue.json { // ...
- import downloadjs from 'downloadjs' 如果是自己写的函数 没用默认导出 记得加花括号 例如 import { download } from './data.js'
import downloadjs from 'downloadjs' 如果是自己写的函数 没用默认导出 记得加花括号 例如 import { download } from './data.js'
- Java集合(五)--LinkedList源码解读
首先看一下LinkedList基本源码,基于jdk1.8 public class LinkedList<E> extends AbstractSequentialList<E> ...
- 安卓获取数据demo出现的问题
时间戳是long型的数据,但其他数据都是float型,但AsyncTask要求是统一数据类型.这样我就不能把时间戳放进AsyncTask里面进行处理,我就在doInBackground中获取时间戳然后 ...
- 暑假集训 || 区间DP
区间DP 经典石子合并问题V1 复杂度 On3 int a[SZ], sum[SZ], f[SZ][SZ]; int main() { int n; scanf("%d", ...
- 32位和64位系统下 int、char、long、double所占的内存
32位和64位系统下 int.char.long.double所占内存
- C# string补位
参考:https://www.cnblogs.com/zhangqs008/archive/2012/02/01/2341078.html //1.1.左补位 "; , 'A'); //1. ...
- 远程桌面mstsc关闭连接栏
在进行mstsc远程桌面连接电脑或者虚拟机的时候,总是会出现一个连接栏.虽然点左边的图钉可以自动隐藏,但是每次鼠标滑到上面的时候,还是会冒出来,这个就有点烦心了. 查了下资料,解决了这个问题. 关闭步 ...