【学习笔记】虚树复习记(BZOJ2286 SDOI2011 消耗战)
想写战略游戏却想不起来虚树T^T
所以就有了这篇复习记QwQ
——简介!——
我们在处理树上问题的时候,dfs是一个常用手段,但是我们发现,如果一棵树上只有一部分关键点,每次dfs需要访问好多不是关键的点,就很浪费时间。所以虚树就被发明出来啦!看到一个非常好的解释,虚树就是通过简化树的形态来进行dfs从而加快效率。
他处理的问题中很多都是 这样的,其中k表示询问节点个数。
建出的虚树中包含所有的关键点 和一些非关键点连接关键点来维护原树中关键点的相对形态
说起来有一点玄学,所以我们直接来看怎么构造虚树的吧。
前缀芝士
1.DFS序
2.倍增
3.树形DP
——构造!——
首先,我们将询问的关键点按照原树中的DFS序排序。
我们用一个栈来辅助构造虚树。
记住这几句话:压栈不连边,弹栈必连边。栈中一条链,深度是递增。【我好会编口诀啊】
(是栈底到栈顶递增qwq)
接下来我们来讨论一下3种情况。(这里配合画图食用口味更佳!)
[x:当前要加入栈中的元素 y:栈顶的元素 lca:x,y的lca z:栈中栈顶下方的元素]
1. lca 为 y
说明x位于y子树内 直接把x压入栈内 表示x挂到位于y下方的链上。
2. lca 既不是 x 也不是 y
表示x,y分别存在于 lca 的两侧 分为两棵子树
这时候 我们发现对于y子树应该是需要构造完 我们才可以把x压入栈内 不然的话就不是一条链了
这时候我们继续讨论几种情况。
(1)dfn[z]>dfn[lca] 说明y和z之间不会再出现新的节点 直接弹栈连边。
(2)z==lca z就是lca 表示 y和z之间没有新的点 弹栈连边 并且退出循环。
(3)dfn[z]<dfn[lca] 说明lca应该是作为新建节点 并且将来要和x连边[还是位于一条链] 那么将y弹栈连边,退出循环
3. lca 为 x
这种情况不会存在,因为我们按照dfs序排序了,所以x一定不会是y的祖先。
还有一个很重要的地方!
我们建虚树的时候 只是使用了一些关键节点,所以复杂度是低于O(n)的 如果清空数组使用了memset就是O(n)然后就萎啦
所以一个小技巧就是自杀式遍历 或者存下使用的节点 删除即可
——例题——
直接建虚树 然后熟悉的树形dp
令f[i]表示 i子树内部的所有点和1断开的最小代价
如果i是关键点 那么f[i]=dis[i] (1-i路径上的最小值)
如果i不是关键点 那么 f[i]=min(dis[i],sum(f[v])) [v是i的儿子]
然后就可以做啦~
附代码。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define inf 20021225
#define ll long long
#define mxn 2500100
using namespace std;
struct edge{int to,lt;ll v;}e[mxn<<1],p[mxn<<1];
int in[mxn],cnt,cnp,ip[mxn],stk[mxn];
int ff[mxn][20],dfn[mxn],tot;
ll f[mxn],dis[mxn];int n,m,dep[mxn];
void add(int x,int y)
{
e[++cnt].to=y;e[cnt].lt=in[x];in[x]=cnt;
}
void app(int x,int y,ll v)
{
p[++cnp].to=y;p[cnp].lt=ip[x];p[cnp].v=v;ip[x]=cnp;
p[++cnp].to=x;p[cnp].lt=ip[y];p[cnp].v=v;ip[y]=cnp;
}
void dfs(int x)
{
dfn[x]=++tot;
for(int i=1;i<20;i++)
ff[x][i]=ff[ff[x][i-1]][i-1];
for(int i=ip[x];i;i=p[i].lt)
{
int y=p[i].to;if(dfn[y]) continue;
ff[y][0]=x;dis[y]=min(dis[x],p[i].v);
dep[y]=dep[x]+1;dfs(y);
}
}
int LCA(int x,int y)
{
if(dep[x]<dep[y]) swap(x,y);
int len=dep[x]-dep[y];
for(int i=19;~i;i--) if(len&(1<<i)) x=ff[x][i];
if(x==y) return x;
for(int i=19;~i;i--)
if(ff[x][i]!=ff[y][i])
x=ff[x][i],y=ff[y][i];
return ff[x][0];
}
int poi[mxn];bool spc[mxn];
bool cmp(int x,int y){return dfn[x]<dfn[y];}
void insert(int x)
{
if(!stk[0]){stk[++stk[0]]=x;return;}
int lca=LCA(stk[stk[0]],x);
if(lca!=stk[stk[0]])
while(stk[0]>1)
{
int z=stk[stk[0]-1];
if(dfn[z]>dfn[lca])
add(z,stk[stk[0]]),stk[0]--;
else if(z==lca)
{
add(z,stk[stk[0]]),stk[0]--;
break;
}
else if(dfn[z]<dfn[lca])
{
add(lca,stk[stk[0]]);
stk[stk[0]]=lca;break;
}
}
if(stk[stk[0]]!=x) stk[++stk[0]]=x;
}
void build(int q)
{
sort(poi+1,poi+q+1,cmp);
cnt=0;stk[stk[0]=1]=1;
for(int i=1;i<=q;i++)
//if(poi[i]==1) continue;
insert(poi[i]);
while(stk[0]>1)
add(stk[stk[0]-1],stk[stk[0]]),stk[0]--;
}
void dp(int x)
{
f[x]=100000000000ll;ll sum=0;
for(int &i=in[x];i;i=e[i].lt)
{
int y=e[i].to;
dp(y);sum+=f[y];
}
if(spc[x]) f[x]=dis[x];
else f[x]=min((ll)dis[x],sum);
spc[x]=0;
}
int main()
{
int x,y,q;ll v;
scanf("%d",&n);
for(int i=1;i<n;i++) scanf("%d%d%lld",&x,&y,&v),app(x,y,v);
scanf("%d",&m);
memset(dis,48,sizeof(dis));
dep[1]=1;dfs(1);
for(int i=1;i<=m;i++)
{
scanf("%d",&q);
for(int j=1;j<=q;j++)
{
scanf("%d",&x);
spc[x]=1;poi[j]=x;
}
build(q);dp(1);
printf("%lld\n",f[1]);
}
return 0;
}
【学习笔记】虚树复习记(BZOJ2286 SDOI2011 消耗战)的更多相关文章
- mybatis学习笔记之基础复习(3)
mybatis学习笔记之基础复习(3) mybatis是什么? mybatis是一个持久层框架,mybatis是一个不完全的ORM框架.sql语句需要程序员自己编写, 但是mybatis也是有映射(输 ...
- [BZOJ2286][SDOI2011]消耗战(虚树DP)
2286: [Sdoi2011]消耗战 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 4998 Solved: 1867[Submit][Statu ...
- BZOJ2286 [Sdoi2011]消耗战 【虚树 + 树形Dp】
2286: [Sdoi2011]消耗战 Time Limit: 20 Sec Memory Limit: 512 MB Submit: 4261 Solved: 1552 [Submit][Sta ...
- [Bzoj2286][Sdoi2011]消耗战(虚树模板题附讲解)
2286: [Sdoi2011]消耗战 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 4896 Solved: 1824[Submit][Statu ...
- bzoj2286: [Sdoi2011]消耗战 虚树
在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个 ...
- 2018.09.25 bzoj2286: [Sdoi2011]消耗战(虚树+树形dp)
传送门 又一道虚树入门题. 这个dp更简单啊. 直接记录每个点到1的距离,简单转移就行了. 代码: #include<bits/stdc++.h> #define N 250005 #de ...
- BZOJ2286:[SDOI2011]消耗战(树形DP,虚树)
Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军 ...
- BZOJ2286: [Sdoi2011]消耗战(虚树/树形DP)
Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 5246 Solved: 1978[Submit][Status][Discuss] Descript ...
- [BZOJ2286][Sdoi2011]消耗战(虚树上DP)
2286: [Sdoi2011]消耗战 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 6457 Solved: 2533[Submit][Statu ...
随机推荐
- .HDF数据库与SQLSERVER / ORACLE的区别
无论ArcGIS的.gbd文件还是MapGIS的.hdf文件,都是数据库文件. 后缀是无意义的.有意义的是其中内在的逻辑和数据结构. https://zhidao.baidu.com/question ...
- 笨办法学Python(learn python the hard way)--练习程序31-35
下面是练习31-练习35,基于python3 #ex31.py 1 print("You enter a dark room witn two doors. Do you go throug ...
- Python分析《武林外传》
我一向比较喜欢看武侠电影.小说,但是06年武林外传开播的时候并没有追剧,简单扫几眼之后发现他们都不会那种飞来飞去的打,一点也不刺激.09年在南京培训的时候,日子简单无聊透顶,大好的周末不能出门,只能窝 ...
- loj#2333 「JOI 2017 Final」准高速电车
分析 我们发现到达一个点一定是先快车再准快车再慢车 于是快车将1-n分为多个区间 每次取出每个区间当前能到达的点的数量 选剩余时间贡献最大的的一个取得贡献并且再能到达的最远点建立准快车 代码 #inc ...
- 关于linq中的dbml文件中的对象加s去s的问题
点击工具->选项->数据库工具->O/R Designer ,右面有个启用,如果是true
- StringTokenizer拆分字符串
今天要做一个过滤特殊字符的需求, 看了下公司以前过滤特俗字符代码, 用的居然是 StringTokenizer, 完全不熟悉啊, 于是恶补了一下, 把StringTokenizer在JDK中的文档也翻 ...
- day26—JavaScript对CSS样式的获取和修改实践
转行学开发,代码100天——2018-04-11 通过JavaScript获取和修改HTML元素及CSS属性是其一个基本功能.对于CSS样式通常有行内样式,外部样式,内嵌样式之分. 如: 行内样式: ...
- idea中配置Resin运行环境
文章目录 背景 下载resin 配置idea 背景 为了能够读Resin的源码,只看源码看不到值,故想在idea中通过断点查看. 下载resin https://caucho.com/products ...
- MYSQL学习笔记/2019
以下内容均为转载,只是方便今后学习:如有不妥,请联系删除:特此感谢原博主精心制作!(文章底部贴有原文链接) /* 启动MySQL */ net start mysql /* 连接与断开服务器 */ m ...
- deb包转换为rpm包格式
在Debian系列中安装软件包可以使用apt或者dpkg安装deb包,但是在CentOs, Redhat等则只能安装RPM包,如果希望在Redhat或者CentOS下也安装Deb包的话是不可行的, 但 ...