虚树 virtual-tree

我们发现,如果一棵树中真正需要处理的点很少,而总共点数很多时,可以只处理那些需要的点,而忽略其他点。

因此我们可以根据那些需要的点构建虚树,只保留关键点。

oi-wiki上对虚树的介绍

我们根据一下方式建立虚树:

  • 现将所有需要处理的关键按照欧拉序排序。

  • 用一个栈维护一条从根节点到上一个处理过个点的链(虚树上的链)。

  • 考虑将一个新的点加入虚树:

    • 求出这个点与栈顶元素的 \(Lca\)

    • 如果 \(Lca\) 不是栈顶元素:

      • 在栈中只保留栈中的链与现在加入点所在的链的公共部分加上一个上一次处理完的链中元素(通过 \(Lca\)\(dfn\) )。

      • 如果 \(Lca\) 已经在栈中,则弹出那个多余的元素。

      • 如果 \(Lca\) 还不在栈中,则将 \(Lca\) 与多余元素连边,并加入 \(Lca\)

    • 把新的点加入栈中。

    • 处理完后把栈中的链也连边连上。

注意:由于整个图需要用到的边与点很少,所以在每次新建虚树的时候不能全局清空,而是在把一个新的点加入栈中的时候清空这个点连过的边。

建立虚树代码:

inline void build()
{sort(query+1,query+m+1,cmp),tot=tp=0;sta[++tp]=1,hea[1]=0;for(int i=1,l;i<=m;i++){if(query[i]==1) continue;l=Lca(query[i],sta[tp]);if(l!=sta[tp]){while(dfn[l]<dfn[sta[tp-1]]){Lca(sta[tp-1],sta[tp]); // 这里用来处理这一条虚树中的边的权值。add(sta[tp-1],sta[tp],minn);tp--;}if(sta[tp-1]!=l){hea[l]=0;Lca(l,sta[tp]); // 这里用来处理这一条虚树中的边的权值。add(l,sta[tp],minn);sta[tp]=l;}else{Lca(l,sta[tp]); // 这里用来处理这一条虚树中的边的权值。add(l,sta[tp--],minn);}}hea[query[i]]=0,sta[++tp]=query[i];}for(int i=1;i<tp;i++){Lca(sta[i],sta[i+1]); // 这里用来处理这一条虚树中的边的权值。add(sta[i],sta[i+1],minn);}
}

建立完之后就可以在虚树上处理答案了,这样即使多次询问,复杂度也是和选中关键点个数同阶的。

例题:

  • P2495 [SDOI2011]消耗战

  • P3233 [HNOI2014]世界树

世界树 $-\texttt{code}$
// Author:A weak man named EricQian
#include<bits/stdc++.h>
using namespace std;
#define inf 0x3f3f3f3f
#define Maxn 300005
#define Maxpown 21
#define pb push_back
#define pa pair<int,int>
#define fi first
#define se second
typedef long long ll;
inline int rd()
{int x=0;char ch,t=0;while(!isdigit(ch = getchar())) t|=ch=='-';while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();return x=t?-x:x;
}
int n,m,q,tot,Time,tp;
struct Dot{ int num,pointnum; }query[Maxn];
int dep[Maxn],siz[Maxn],dfn[Maxn],sta[Maxn],fa[Maxn][Maxpown]; // 处理虚树 
bool exist[Maxn]; // 处理虚树 
int ans[Maxn],belson[Maxn],hea[Maxn],nex[Maxn],ver[Maxn],edg[Maxn]; // 虚树 
// belson[v] : u 是 v 在 虚树上的父亲,belson 是 v 属于 u 的那一个原图上的儿子 
pa Near[Maxn];
vector<int> g[Maxn]; // 原图 
void dfspre(int x)
{dfn[x]=++Time,siz[x]=1;for(int v:g[x]){if(v==fa[x][0]) continue;fa[v][0]=x,dep[v]=dep[x]+1;for(int i=1;i<=20;i++) fa[v][i]=fa[fa[v][i-1]][i-1];dfspre(v);siz[x]+=siz[v];}
}
inline int Lca(int x,int y)
{if(dep[x]>dep[y]) swap(x,y);for(int i=20;i>=0;i--) if(dep[fa[y][i]]>=dep[x]) y=fa[y][i];if(x==y) return x;for(int i=20;i>=0;i--) if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];return fa[x][0];
}
inline void add(int x,int y,int d){ ver[++tot]=y,nex[tot]=hea[x],hea[x]=tot,edg[tot]=d; }
bool cmp1(Dot x,Dot y){ return dfn[x.pointnum]<dfn[y.pointnum]; }
bool cmp2(Dot x,Dot y){ return x.num<y.num; }
inline void build()
{m=rd();for(int i=1;i<=m;i++) query[i]=(Dot){i,rd()},exist[query[i].pointnum]=true;sort(query+1,query+m+1,cmp1);tot=0,sta[tp=1]=1,hea[1]=0;for(int i=1,l;i<=m;i++){if(query[i].pointnum==1) continue;l=Lca(sta[tp],query[i].pointnum);if(l!=sta[tp]){while(dfn[l]<dfn[sta[tp-1]])add(sta[tp-1],sta[tp],dep[sta[tp]]-dep[sta[tp-1]]),tp--;if(sta[tp-1]!=l)hea[l]=0,add(l,sta[tp],dep[sta[tp]]-dep[l]),sta[tp]=l;else add(l,sta[tp],dep[sta[tp]]-dep[l]),tp--;}hea[query[i].pointnum]=0,sta[++tp]=query[i].pointnum;}for(int i=1;i<tp;i++) add(sta[i],sta[i+1],dep[sta[i+1]]-dep[sta[i]]);
}
void dfs1(int x) // 处理最近的特殊点-1 (+init)
{if(exist[x]) Near[x]=pa(0,x);else Near[x]=pa(inf,inf);ans[x]=0;for(int i=hea[x],tmp,Now;i;i=nex[i]){dfs1(ver[i]);Now=Near[x].fi,tmp=Near[ver[i]].fi+edg[i];if((tmp<Now) || (tmp==Now && Near[ver[i]].se<Near[x].se))Near[x].fi=tmp,Near[x].se=Near[ver[i]].se;}
}
void dfs2(int x) // 处理最近的特殊点-2
{for(int i=hea[x],Now,tmp;i;i=nex[i]){Now=Near[x].fi+edg[i],tmp=Near[ver[i]].fi;if((Now<tmp) || (Now==tmp && Near[x].se<Near[ver[i]].se))Near[ver[i]].fi=Now,Near[ver[i]].se=Near[x].se;dfs2(ver[i]);}
}
void dfs3(int x) // 把在虚树外的点计算(通过倍增到父亲的下面) 
{int All=siz[x];for(int i=hea[x],tmp;i;i=nex[i]){tmp=ver[i];for(int j=20;j>=0;j--) if(dep[fa[tmp][j]]>dep[x]) tmp=fa[tmp][j];All-=siz[tmp],belson[ver[i]]=tmp;dfs3(ver[i]);}ans[Near[x].se]+=All;
}
void dfs4(int x) // 计算中间的点 
{for(int i=hea[x];i;i=nex[i]){if(Near[x].se==Near[ver[i]].se)ans[Near[x].se]+=siz[belson[ver[i]]]-siz[ver[i]];else{int downdep=dep[Near[ver[i]].se]+dep[x]-Near[x].fi;if(downdep & 1) downdep=downdep/2+1;else downdep=(Near[x].se<Near[ver[i]].se)?(downdep/2+1):(downdep/2);int tmp=ver[i];for(int j=20;j>=0;j--) if(dep[fa[tmp][j]]>=downdep) tmp=fa[tmp][j];ans[Near[x].se]+=siz[belson[ver[i]]]-siz[tmp];ans[Near[ver[i]].se]+=siz[tmp]-siz[ver[i]];}dfs4(ver[i]);}
}
int main()
{//ios::sync_with_stdio(false); cin.tie(0);//freopen(".in","r",stdin);//freopen(".out","w",stdout);n=rd();for(int i=1,x,y;i<n;i++) x=rd(),y=rd(),g[x].pb(y),g[y].pb(x);dep[1]=1,dfspre(1),q=rd();for(int i=1;i<=q;i++){build();dfs1(1),dfs2(1); // 处理好最近的点 dfs3(1),dfs4(1); // 计算答案 sort(query+1,query+m+1,cmp2);for(int j=1;j<=m;j++) printf("%d%c",ans[query[j].pointnum],(j==m)?'\n':' ');for(int j=1;j<=m;j++) exist[query[j].pointnum]=false;}//fclose(stdin);//fclose(stdout);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/318215.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

8.16模拟:树上算法

文章目录前言收获全排列求期望模型转化树哈希判断同构&无根同构转有根同构比赛复盘T1 reformT2 buildT3 relationT4 split总结前言 150分 10020300 qwq 今天题还是较难 而且又去打了半个多小时的疫苗 情有可原吧 T2其实是可切的 T3的暴力因为数组开小了挂了30qwq 感觉这几…

【用学校抄作业带你走进可持久化线段树(主席树)】可持久化线段树概念+全套模板+例题入门:[福利]可持久化线段树)

我似乎很少写这种算法博客可持久化线段树概念概念介绍&#xff08;类比帮助理解&#xff09;简单分析一下时间和空间复杂度&#xff08;内容池&#xff09;模板结构体变量建树模板单点修改模板单点查询模板区间修改模板&#xff08;pushup&#xff09;区间修改模板&#xff08;…

bzoj#4161-Shlw loves matrixI【常系数线性齐次递推】

正题 题目链接:https://darkbzoj.tk/problem/4161 题目大意 给出序列aaa&#xff0c;和hhh的0∼k−10\sim k-10∼k−1项&#xff0c;满足 hn∑i1naihn−ih_n\sum_{i1}^na_ih_{n-i}hn​i1∑n​ai​hn−i​ 求hnh_nhn​。 1≤n≤109,1≤k≤20001\leq n\leq 10^9,1\leq k\leq 20…

P3258 [JLOI2014]松鼠的新家

文章目录题意&#xff1a;题解&#xff1a;树上差分代码&#xff1a;树链剖分代码&#xff1a;P3258 [JLOI2014]松鼠的新家题意&#xff1a; n个点&#xff0c;n-1条边&#xff0c;给出每个点的拜访顺序&#xff0c;问每个点经过几次&#xff08;最后一次移动不算拜访&#xf…

8.17模拟:数学

文章目录前言收获考场复盘T1T2T3T4总结前言 190分 60100300 虽然分不太高&#xff0c;但毕竟今天的题有点太阴间了… 所以还不错啦 最重要的是今天挂分很少 终于停住了这几天越挂越嗨的态势 也就T4挂了5分吧&#xff0c;可以接受 收获 算斜率的区间确定一条线旋转位置时要取…

牛客-小w的魔术扑克【并查集】

正题 题目链接:https://ac.nowcoder.com/acm/contest/1100/C 题目大意 nnn个数字mmm张扑克牌&#xff0c;每张两面有各有一个数字&#xff0c;可以选择一些扑克牌使用正面的数字&#xff0c;一些使用反面的&#xff0c;qqq次询问能否凑出l∼rl\sim rl∼r。 1≤n,m,q≤1051\leq…

[SOCI2005]最大子矩阵(DP) + [JXOI2018]守卫(DP) + [CQOI2016]手机号码(数位DP)[各种DP专练]

DP专练博客 DP专练T1&#xff1a;最大子矩阵题目题解代码实现T2&#xff1a;守卫题目题解代码实现T3&#xff1a;手机号码题目题解代码实现T1&#xff1a;最大子矩阵 题目 这里有一个n*m的矩阵&#xff0c;请你选出其中k个子矩阵&#xff0c;使得这个k个子矩阵分值之和最大。…

【做题记录】位运算

CF1592E Bored Bakry 题意&#xff1a;找出最长的区间 \([l,r]\) 满足 \(a_{l}\&a_{l1}\&\dots\&a_{r-1}\&a_{r}>a_{l}\oplus a_{l1}\oplus\dots\oplus a_{r-1}\oplus a_{r}\) 。 首先发现如果有一段满足这个条件的区间&#xff0c;那么一定有一个(较高的)二…

IdentityServer4-EF动态配置Client和对Claims授权(二)

本节介绍Client的ClientCredentials客户端模式&#xff0c;先看下画的草图&#xff1a;一、在Server上添加动态新增Client的API 接口。为了方便测试&#xff0c;在Server服务端中先添加swagger&#xff0c;添加流程可参考&#xff1a;https://www.cnblogs.com/suxinlcq/p/67575…

P3178 [HAOI2015]树上操作

P3178 [HAOI2015]树上操作 题意&#xff1a; 题解&#xff1a; 这已经是很裸的树链剖分了。。。 直接套模板 代码&#xff1a; #include<cmath> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespac…

8.18模拟:构造

文章目录前言收获考场复盘T1T2T3T4总结前言 190分 10006030 明明是dfs专题 不太理想qwq 写了三个dfs就离谱 最不满意的是T2的爆零 其实分类讨论一下是很可做的 而且暴力还因为没开ll挂掉了… 不过毕竟构造题之前几乎没有做过 所以慢慢来吧 收获 一些构造题的trick 调整法数…

P5371-[SNOI2019]纸牌【矩阵乘法】

正题 题目链接:https://www.luogu.com.cn/problem/P5371 题目大意 有nnn种牌&#xff0c;每种牌最多CCC张&#xff0c;XXX个限制形如kik_iki​种牌至少aia_iai​张。 求所有牌的序号能分成(i,i,i)(i,i,i)(i,i,i)或者(i,i1,i2)(i,i1,i2)(i,i1,i2)的若干组的方案数。 1≤n≤10…

dotnet core开源博客系统XBlog介绍

XBlog是dotnet core平台下的个人博客开源系统&#xff0c;它只需要通过Copy的方式即可以部署到Linux和windows系统中&#xff1b;如果你有安全证书那只需要简单配置一下即可提供安全的Https服务。接下来主要介绍XBlog功能、部署和基础设置。技术要点基于dotnet core平台&#x…

【做题记录】max-min+1=len 区间计数

(来源&#xff1a;XJ高质量原创题) 原题地址 弱化版&#xff1a;CF526F Pudding Monsters 弱化版 题意&#xff1a;\(n\times n\) 的棋盘上有 \(n\) 颗棋子&#xff0c;每行每列都有且仅有一颗棋子&#xff0c;求出有多少个 \(k\times k\) 的子棋盘也满足每行每列只有一颗棋子。…

【莫队/树上莫队/回滚莫队】原理详解及例题:小B的询问(普通莫队),Count on a tree II(树上莫队),kangaroos(回滚莫队)

文章目录问题引入介绍莫队算法及其实现过程时间复杂度莫队算法适用范围莫队奇偶优化普通莫队&#xff1a;小B的询问树上莫队&#xff1a;SP10707 COT2 - Count on a tree II回滚莫队&#xff1a;[PA2011]Kangaroosupd&#xff1a;2021-08-11&#xff1a;重新对博客进行了外观美…

P2146 [NOI2015] 软件包管理器

P2146 [NOI2015] 软件包管理器 题意&#xff1a; 如果软件包 a 依赖软件包 b&#xff0c;那么安装软件包 a 以前&#xff0c;必须先安装软件包 b。同时&#xff0c;如果想要卸载软件包 b&#xff0c;则必须卸载软件包 a。 软件包之间存在依赖关系&#xff0c;除了0号软件包以…

微软 2018 开源大事记

从微软公开宣布 "Microsoft love linux" 那一刻起&#xff0c;过去的几年里&#xff0c;微软积极拥抱开源的举动我们有目共睹&#xff0c;即便有过"Linux is a cancer"这种真香警告的 flag&#xff0c;但不得不承认的是&#xff0c;微软一系列“拥抱开源”…

模板:二叉搜索树平衡树

文章目录前言二叉搜索树代码treap代码splay开点旋转splay插入查找第k大元素查找给定元素的排名前驱&后继删除完整代码练习总结前言 终于开始学这个东西了 看了好几篇博客才找到一篇可读的qwq 我曾经还以为线段树码量大…我真傻&#xff0c;真的 所谓平衡树&#xff0c;就是…

51nod2626-未来常数【树上启发式合并,线段树】

正题 题目链接:http://www.51nod.com/Challenge/Problem.html#problemId2626 题目大意 给出nnn个点的一棵树&#xff0c;每个区间[l,r][l,r][l,r]的代价是选出这个区间中的一个点xxx使得它走到所有点然后又回到xxx的路程最短长度&#xff0c;求一个随机区间的期望代价。 1≤n…

A*,IDA*—高档次的暴搜

A*通过评价函数来判断当前状态是否可以到达最终状态(即可行性剪枝)&#xff0c;来减少不必要的搜索。 例题——P2324 [SCOI2005]骑士精神 我们通过当前不在指定位置上的棋子个数为评价函数&#xff0c;\(used\) 【评价函数值】超过了预期的值&#xff0c;便不用再线下深入搜索了…