简介

虚树可以解决一些关于树上一部分节点的问题. 对于一棵树 \(T\) 的一个子集 \(S\), 可以在 \(O(|S| \log |S|)\) 的时间复杂度内求出 \(S\) 的虚树.

虚树包括根节点, 所有询问点和所有询问点之间的 \(lca\).

代码

//store the tree
struct tg{
struct te{int t,pr,v;}edge[nsz*2];
int hd[nsz],pe=1;
void adde(int f,int t,int v){edge[++pe]=(te){t,hd[f],v};hd[f]=pe;}
void adddb(int f,int t,int v){adde(f,t,v);adde(t,f,v);}
#define forg(g,p,i,v) for(int i=g.hd[p],v=g.edge[i].t;i;i=g.edge[i].pr,v=g.edge[i].t)
void clear(){//only g2
pe=1;
// rep(i,1,pu)hd[usedp[i]]=0;
}
}g1,g2; //get lca (with sparse table)
namespace nlca{
void dfs(int u,int fa){
eul[++pe]=u,vis[u]=pe,d[u]=d[fa]+1;
forg(g1,u,i,v){
if(v==fa)continue;
dfs(v,u);
eul[++pe]=u;
}
}
int dmin(int a,int b){return d[a]<=d[b]?a:b;}
void rmq(){
rep(i,1,pe)stt[i][0]=eul[i];
rep(j,1,l2n[pe]){
rep(i,1,pe+1-(1<<j)){
stt[i][j]=dmin(stt[i][j-1],stt[i+(1<<(j-1))][j-1]);
}
}
}
int stqu(int a,int b){
int l=l2n[b-a+1];
return dmin(stt[a][l],stt[b-(1<<l)+1][l]);
}
void eulinit(){
int l=0;
rep(i,1,n*3){
if(i==(1<<(l+1)))++l;
l2n[i]=l;
}
dfs(1,0);
rmq();
}
int lca(int a,int b){
int x=vis[a],y=vis[b];
if(x>y)swap(x,y);
return stqu(x,y);
}
} // 求虚树
// line[1...k]: 用到的点
bool cmp(int a,int b){return vis[a]<vis[b];}
int stk[nsz],top=0;
void build(){
g2.clear(),top=0;
sort(line+1,line+k+1,cmp);
top=0,stk[++top]=1;
rep(i,1,k){
int l=nlca::lca(line[i],stk[top]);
while(top>1&&vis[stk[top-1]]>=vis[l])g2.adde(stk[top-1],stk[top],1),--top;
if(l!=stk[top])g2.adde(l,stk[top],1),stk[top]=l;
stk[++top]=line[i];
}
while(top>1)g2.adde(stk[top-1],stk[top],1),--top;
} //dfs 过程
void sol(int p){
forg(g2,p,i,v){
sol(v);
//do something...
}
g2.hd[p]=0; //清空虚树
}

例题: BZOJ2286 [Sdoi2011]消耗战

建立虚树之后dp即可.

注意输入的节点必须断掉, 但lca节点可断可不断. 可以标记输入的节点.

#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<set>
#include<map>
using namespace std;
#define rep(i,l,r) for(register int i=(l);i<=(r);++i)
#define repdo(i,l,r) for(register int i=(l);i>=(r);--i)
#define il inline
typedef double db;
typedef long long ll; //---------------------------------------
const int nsz=250050;
const ll ninf=1e18; int n,m,k,line[nsz];
int used[nsz]; struct tg{
struct te{int t,pr,v;}edge[nsz*2];
int hd[nsz],pe=1;
void adde(int f,int t,int v){edge[++pe]=(te){t,hd[f],v};hd[f]=pe;}
void adddb(int f,int t,int v){adde(f,t,v);adde(t,f,v);}
#define forg(g,p,i,v) for(int i=g.hd[p],v=g.edge[i].t;i;i=g.edge[i].pr,v=g.edge[i].t)
void clear(){//only g2
pe=1;
// rep(i,1,pu)hd[usedp[i]]=0;
}
}g1,g2; ll mind[nsz]{0,ninf}; int l2n[nsz*3+50];
int eul[nsz*3],pe=0,vis[nsz],d[nsz];
int stt[nsz*3][21]; namespace nlca{
void dfs(int u,int fa){
eul[++pe]=u,vis[u]=pe,d[u]=d[fa]+1;
forg(g1,u,i,v){
if(v==fa)continue;
mind[v]=min(mind[u],(ll)g1.edge[i].v);
dfs(v,u);
eul[++pe]=u;
}
}
int dmin(int a,int b){return d[a]<=d[b]?a:b;}
void rmq(){
rep(i,1,pe)stt[i][0]=eul[i];
rep(j,1,l2n[pe]){
rep(i,1,pe+1-(1<<j)){
stt[i][j]=dmin(stt[i][j-1],stt[i+(1<<(j-1))][j-1]);
}
}
}
int stqu(int a,int b){
int l=l2n[b-a+1];
return dmin(stt[a][l],stt[b-(1<<l)+1][l]);
}
void eulinit(){
int l=0;
rep(i,1,n*3){
if(i==(1<<(l+1)))++l;
l2n[i]=l;
}
dfs(1,0);
rmq();
}
int lca(int a,int b){
int x=vis[a],y=vis[b];
if(x>y)swap(x,y);
return stqu(x,y);
}
} bool cmp(int a,int b){return vis[a]<vis[b];}
int stk[nsz],top=0;
void build(){
g2.clear(),top=0;
sort(line+1,line+k+1,cmp);
top=0,stk[++top]=1;
rep(i,1,k){
int l=nlca::lca(line[i],stk[top]);
while(top>1&&vis[stk[top-1]]>=vis[l])g2.adde(stk[top-1],stk[top],1),--top;
if(l!=stk[top])g2.adde(l,stk[top],1),stk[top]=l;
stk[++top]=line[i];
}
while(top>1)g2.adde(stk[top-1],stk[top],1),--top;
} ll dp[nsz];
void sol(int p){
dp[p]=0;
forg(g2,p,i,v){
sol(v);
dp[p]+=(used[v]?mind[v]:min(mind[v],dp[v]));
}
g2.hd[p]=0;
} int main(){
ios::sync_with_stdio(0),cin.tie(0);
cin>>n;
int a,b,c;
rep(i,2,n){
cin>>a>>b>>c;
g1.adddb(a,b,c);
}
nlca::eulinit();
cin>>m;
rep(i,1,m){
cin>>k;
rep(j,1,k)cin>>line[j],used[line[j]]=1;
pe=1;
build();
sol(1);
cout<<dp[1]<<'\n';
rep(j,1,k)used[line[j]]=0;
}
return 0;
}

[模板] 虚树 && bzoj2286-[Sdoi2011]消耗战的更多相关文章

  1. [Bzoj2286][Sdoi2011]消耗战(虚树模板题附讲解)

    2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 4896  Solved: 1824[Submit][Statu ...

  2. [BZOJ2286][SDOI2011]消耗战(虚树DP)

    2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 4998  Solved: 1867[Submit][Statu ...

  3. BZOJ2286 [Sdoi2011]消耗战 【虚树 + 树形Dp】

    2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 4261  Solved: 1552 [Submit][Sta ...

  4. 【学习笔记】虚树复习记(BZOJ2286 SDOI2011 消耗战)

    想写战略游戏却想不起来虚树T^T 所以就有了这篇复习记QwQ ——简介!—— 我们在处理树上问题的时候,dfs是一个常用手段,但是我们发现,如果一棵树上只有一部分关键点,每次dfs需要访问好多不是关键 ...

  5. bzoj2286: [Sdoi2011]消耗战 虚树

    在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个 ...

  6. 2018.09.25 bzoj2286: [Sdoi2011]消耗战(虚树+树形dp)

    传送门 又一道虚树入门题. 这个dp更简单啊. 直接记录每个点到1的距离,简单转移就行了. 代码: #include<bits/stdc++.h> #define N 250005 #de ...

  7. BZOJ2286:[SDOI2011]消耗战(树形DP,虚树)

    Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军 ...

  8. BZOJ2286: [Sdoi2011]消耗战(虚树/树形DP)

    Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 5246  Solved: 1978[Submit][Status][Discuss] Descript ...

  9. [BZOJ2286][Sdoi2011]消耗战(虚树上DP)

    2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6457  Solved: 2533[Submit][Statu ...

随机推荐

  1. 原生JS编写兼容IE6,7,8浏览器无缝自动轮播(带按钮切换)

    项目要求页面兼容IE6,7,8等浏览器,我们可能会遇到这个轮播效果,轮播板块要求:无限循环.自动轮播和手动切换功能,每一次滚动一小格,网上有很多这类插件,例如:swiper等! 但是很多都是不兼容IE ...

  2. 经典面试题:浏览器是怎样解析CSS的?

    摘要: 理解浏览器原理. 解析 一旦 CSS 被浏览器下载,CSS 解析器就会被打开来处理它遇到的任何 CSS.这可以是单个文档内的 CSS.<style>标记内的 CSS,也可以是 DO ...

  3. JS实现图片base64转blob对象,压缩图片,预览图片,图片旋转到正确角度

    base64转blob对象 /** 将base64转换为文件对象 * @param {String} base64 base64字符串 * */ var convertBase64ToBlob = f ...

  4. 41.Odoo产品分析 (四) – 工具板块(10) – 问卷(1)

    查看Odoo产品分析系列--目录 在该模块下,可以创建问卷,收集答案,打印统计.  安装"问卷"模块,首页显示当前各个阶段中的问卷:  打开"开发者模式",能对 ...

  5. $符号报not defing 报错

    https://blog.csdn.net/weixin_37969488/article/details/84250833 最近因为工作问题,需要我把别的项目上的一些jsp网页copy到新项目上.放 ...

  6. Xutils, OKhttp, Volley, Retrofit对比

    Xutils这个框架非常全面,可以进行网络请求,可以进行图片加载处理,可以数据储存,还可以对view进行注解,使用这个框架非常方便,但是缺点也是非常明显的,使用这个项目,会导致项目对这个框架依赖非常的 ...

  7. 常用Shell脚本命令(备忘)

    此处纪录一些个人常用的Shell命令,留作复用 Linux 必备软件 Tmux 终端复用神器 zsh 无比强大Shell运行环境 oh my zsh 搭配zsh食用 uGet Linux下载工具 Do ...

  8. python爬虫实战:利用scrapy,短短50行代码下载整站短视频

    近日,有朋友向我求助一件小事儿,他在一个短视频app上看到一个好玩儿的段子,想下载下来,可死活找不到下载的方法.这忙我得帮,少不得就抓包分析了一下这个app,找到了视频的下载链接,帮他解决了这个小问题 ...

  9. ORACLE中如何查找定位表最后DML操作的时间小结

    在Oracle数据库中,如何查找,定位一张表最后一次的DML操作的时间呢? 方式有三种,不过都有一些局限性,下面简单的解析.总结一下. 1:使用ORA_ROWSCN伪列获取表最后的DML时间 ORA_ ...

  10. MySQL常用字符串函数

    字符串函数 是最常用的的一种函数,在一个具体应用中通常会综合几个甚至几类函数来实现相应的应用: 1.LOWER(column|str):将字符串参数值转换为全小写字母后返回 mysql> sel ...