题意

[SDOI2011]消耗战

想法

首先我们可以很自然的想到怎么在整棵树上进行求解\(DP\)

很简单 每个点有两个选择 要么对其子树的关键点递归求解 要么自己断开

当然断开的\(cost\)为其到根的最短边权

但我们发现每次都进行一次\(O(n)\)的整棵树\(DP\)我们实在是不太敢用

于是这个时候虚树起到了他的作用

给出定义:虚树是一些关键点按照在原树的祖孙关系构建的树

\(当A在原树中是B的祖先,那么在虚树中任然是\)

同时虚树中不仅有关键点,为了保持其原有的一些信息,一些关键点的\(LCA\)仍是必要存在的

芝士:虚树

我们怎么构建这个虚树呢,我们首先对每个点标号\(dfn\),然后对关键点按标号排序

这样保证我们是一条一条链加进来的

我们逐个加入

1.如果栈为空,或者栈中只有一个元素,那么显然应该:

\(stk[++top]=u;\)

2.取\(lca=LCA(u,stk[top])\),如果\(lca=stk[top]\)则说明\(u\)点应该接着\(stk[top]\)点延长当前的树链.做操作:

\(stk[++top]=u;stk[++top]=u;\)

3.如果\(lca≠stk[top]\)则说明\(u与stk[top]\)分属\(lca\)的两颗不同的子树,且包含\(stk[top]\)的这颗子树应该已经构建完成了,我们需要做的是:

将\(lca\)的包含\(stk[top]\)子树的那部分退栈,并将这部分建边形成虚树.如果\(lca\)不在栈(树链)中,那么要把\(lca\)也加入栈中,保证虚树的结构不出现问题,随后将u加入栈中,以表延长树链.

这里建议画图思考

建完虚树在虚树上跑就行了

代码

#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
#define ll long long ll n,k; struct P{
int to,next,v;
}; int dfn[50005],mn[50005];
ll cnt = 0; struct T{
int head[25010];
P e[500010];
void add(ll x,ll y,ll v){
e[++cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt;
e[cnt].v = v;
}
int d[25010],fa[25010],son[25010],s[25010];
void dfs(ll now,ll f){
d[now] = d[f] + 1;
fa[now] = f;
ll maxx = 0;
s[now] = 1;
for(int i = head[now];i;i = e[i].next){
if(e[i].to == f)continue;
mn[e[i].to] = std::min(mn[now],e[i].v);
dfs(e[i].to,now);
s[now] += s[e[i].to];
if(s[e[i].to] > maxx)
maxx = s[e[i].to],son[now] = e[i].to;
}
}
int dfncnt,top[250010];
void dfs2(ll now,ll tp){
dfn[now] = ++dfncnt;
top[now] = tp;
if(!son[now])return ;
dfs2(son[now],tp);
for(int i = head[now];i;i = e[i].next){
if(e[i].to == fa[now] || e[i].to == son[now])continue;
dfs2(e[i].to,e[i].to);
}
}
ll lca(ll x,ll y){
while(top[x]!=top[y]){
if(d[top[x]]<d[top[y]]) x^=y^=x^=y;
x=fa[top[x]];
}
return d[x]>d[y]?y:x;
}
}Q; ll t;
ll top = 0;
int s[25010]; struct FT{
std::vector<int>son[25010];
void add(ll x,ll y){son[x].push_back(y);}
void ins(ll x){
if(top <= 1) {s[++top] = x;return ;}
ll l = Q.lca(x,s[top]);
if(l == s[top]){return ;}
while(top > 1 && dfn[s[top - 1]] >= dfn[l]){
add(s[top - 1],s[top]);
--top;
}
if(l != s[top]) add(l,s[top]),s[top] = l;
s[++top] = x;
}
ll get(ll now){
if(son[now].size() == 0) return mn[now];
ll ans = 0;
for(register int i = 0;i < son[now].size();++i)
ans += get(son[now][i]);
son[now].clear();
return std::min(ans,(ll)mn[now]);
}
}W; bool cmp(ll x,ll y){return dfn[x] < dfn[y];} int num[250010]; int main(){
memset(mn,0x3f,sizeof(mn));
scanf("%lld",&n);
for(int i = 1;i <= n - 1;++i){
ll x,y,v;
scanf("%lld%lld%lld",&x,&y,&v);
Q.add(x,y,v);
Q.add(y,x,v);
}
Q.dfs(1,0);
Q.dfs2(1,1);
scanf("%lld",&t);
while(t -- ){
ll k;
scanf("%lld",&k);
for(int i = 1;i <= k;++i)
scanf("%d",&num[i]);
std::sort(num + 1,num + k + 1,cmp);
s[top = 1] = 1;
for(int i = 1;i <= k;++i)
W.ins(num[i]);
while(top > 0)W.add(s[top - 1],s[top]),top -- ;
std::cout<<W.get(1)<<std::endl;
}
}

【虚树学习笔记([SDOI2011]消耗战)】的更多相关文章

  1. <虚树+树型DP> SDOI2011消耗战

    <虚树+树型DP> SDOI2011消耗战 #include <iostream> #include <cstdio> #include <cstring&g ...

  2. zkw线段树学习笔记

    zkw线段树学习笔记 今天模拟赛线段树被卡常了,由于我自带常数 \(buff\),所以学了下zkw线段树. 平常的线段树无论是修改还是查询,都是从根开始递归找到区间的,而zkw线段树直接从叶子结点开始 ...

  3. 仙人掌&圆方树学习笔记

    仙人掌&圆方树学习笔记 1.仙人掌 圆方树用来干啥? --处理仙人掌的问题. 仙人掌是啥? (图片来自于\(BZOJ1023\)) --也就是任意一条边只会出现在一个环里面. 当然,如果你的图 ...

  4. 线段树学习笔记(基础&进阶)(一) | P3372 【模板】线段树 1 题解

    什么是线段树 线段树是一棵二叉树,每个结点存储需维护的信息,一般用于处理区间最值.区间和等问题. 线段树的用处 对编号连续的一些点进行修改或者统计操作,修改和统计的复杂度都是 O(log n). 基础 ...

  5. Treap-平衡树学习笔记

    平衡树-Treap学习笔记 最近刚学了Treap 发现这种数据结构真的是--妙啊妙啊~~ 咳咳.... 所以发一发博客,也是为了加深蒟蒻自己的理解 顺便帮助一下各位小伙伴们 切入正题 Treap的结构 ...

  6. JSOI2008 Blue Mary开公司 | 李超线段树学习笔记

    题目链接:戳我 这相当于是一个李超线段树的模板qwqwq,题解就不多说了. 代码如下: #include<iostream> #include<cstdio> #include ...

  7. 算法学习——从bzoj2286开始的虚树学习生活

    [原创]转载请标明原作者~ http://www.cnblogs.com/Acheing/ 题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2 ...

  8. Splay伸展树学习笔记

    Splay伸展树 有篇Splay入门必看文章 —— CSDN链接 经典引文 空间效率:O(n) 时间效率:O(log n)插入.查找.删除 创造者:Daniel Sleator 和 Robert Ta ...

  9. CART分类与回归树 学习笔记

    CART:Classification and regression tree,分类与回归树.(是二叉树) CART是决策树的一种,主要由特征选择,树的生成和剪枝三部分组成.它主要用来处理分类和回归问 ...

随机推荐

  1. UE4蓝图AI角色制作(七)之追逐玩家

    15.追逐玩家 现在我们的AI无法做出任何决策,它总是执行相同的决策.我们先把感知系统中的相关信息提供给AI,让AI知道如何做出决策,然后我们会修改行为树.我们首先需要创建新的黑板键,这样我们就能在行 ...

  2. 天脉2(ACoreOS653)操作系统学习02

    天脉2(ACoreOS653)操作系统学习02 一.分区内通信方法 分区内通信指同一分区内进程之间的通信.ARINC 653定义的分区内进程通信机制,包括:缓存队列(Buffers-Queue).黑板 ...

  3. JVM:内存溢出OOM

    JVM:内存溢出OOM 本笔记是根据bilibili上 尚硅谷 的课程 Java大厂面试题第二季 而做的笔记 经典错误 JVM 中常见的两个 OOM 错误 StackoverflowError:栈溢出 ...

  4. Beta_Scrum Meeting_2

    会议概要 日期:2021年5月30日 出席人员:除zwh以外的所有人员 会议概述:讨论前两天工作进度以及后两天工作计划 人员分工 组员 负责 前两日完成的工作 后两日即将完成的工作 遇到的困难 hcc ...

  5. [no code][scrum meeting] Beta 2

    例会时间:5月14日11:30,主持者:乔玺华 下次例会时间:5月15日11:30,主持者:肖思炀 一.工作汇报 人员 昨日完成任务 明日要完成的任务 乔玺华 - 开issue,分配时间 黎正宇 - ...

  6. TVS管相关知识

    在设计中,使用到了TVS管,在之前的设计中没有特别关注TVS管.今天查了一些资料,算是简单的有个了解. TVS管是一种保护器件.它的英文全称为 transient voltage suppressor ...

  7. 单片机STM32的启动文件详解--学习笔记

    启动文件简介 启动文件由汇编编写,是系统上电复位后第一个执行的程序.主要做了以下工作: 1.初始化堆栈指针SP=_initial_sp 2.初始化PC 指针=Reset_Handler 3.初始化中断 ...

  8. 转:基于 xilinx vivado 的PCIE ip核设置与例程代码详解

    连接:https://blog.csdn.net/u014586651/article/details/103826967#comments

  9. PicGo插件

    前言:主要介绍PicGo插件,这里的图床上传软件是PicGo-Core,使用命令行操作 PicGo_Path:自己的PicGo安装路径,如果通过Typora一般安装位置位于 C:\Users\自己的主 ...

  10. Vue面试题01

    说出vue常用的指令: v-text,  v-html,  v-bind,  v-for,  v-if,  v-else,  v-else-if,   v-show,    v-on, 谈谈你对MVC ...