关于虚树

news/2024/9/28 19:34:46

关于虚树

瞎扯

某些树上问题,给了巨多节点,而实际上它们之中只有小部分能做出贡献,其余都是些水军,为杀尽 OIers的脑细胞 做出努力

考虑重新种一棵树,浓缩信息,简化节点个数,于是产生了 虚树

大概是长这个样子:
红色结点是我们选择的关键点,即能够做出贡献的点。红色和黑色结点都是虚树中的点。黑色的边是虚树中的边。

因为任意两个关键点的 LCA 也是需要保存重要信息的,能够维持树的形态,所以我们需要保存它们的 LCA

显然,在保证爹不会变成儿子,儿子不会变成爹 爷爷也不行 的前提下,我们是可以随便把原有的点添到虚树中去的

你当然可以把原树所有的点都加到虚树中,只不过你这虚树建了跟建了一样

因此,为了方便,我们可以首先将 \(1\) 号节点加入虚树中,并且不会影响答案

构造

构造有两种方式,其中二次排序的方法常数较大,但是好记,也好理解。

另一种,则是借助单调栈实现的。

随个人喜好吧~

二次排序

很直观的思路。

首先将关键点按 \(DFS\) 序排序,枚举每个相邻的关键点,两两求得 \(LCA\) 并加入序列 \(\mathcal A\) 中。

此时序列 \(\mathcal A\) 中包含了虚树中的所有点,但是由于关键点的 \(LCA\) 可能相同,所以仍需去重。

因此我们把序列 \(\mathcal A\) 按照 \(DFS\) 序 从小到大排序并去重。

最后,在序列 \(\mathcal A\) 上,枚举相邻的两个点 \(x,y\),求得它们的 \(LCA\) 并且连接 \(LCA(x,y),y\),即可。

Code

int dfn[N];
int a[N],m,A[N],len;//m为关键点个数bool cmp(int x,int y){return dfn[x]<dfn[y];
} void build(){sort(a+1,a+1+m,cmp);for(int i=1;i<m;i++){A[++len]=a[i];A[++len]=lca(a[i],a[i+1]);}A[++len]=a[m];sort(A+1,A+1+len,cmp);len=unique(A+1,A+1+len)-A-1;for(int i=1;i<len;i++){addnew(lca(A[i],A[i+1]),a[i+1]);}
}

单调栈

直接枚举所有关键点对,暴力求解 LCA 的时间复杂度显然不可接受。

我们可以将所有关键点先按 DFS 序排序,按顺序一个一个加到树里。刚开始树上只有 \(1\) 号点。

接下来,我们用一个栈维护在树上一条链上的所有点。这个栈内的所有点满足其 DFS 序单调递增。

每次要将下一个关键点(设为 \(u\))入栈前,求一下当前栈顶元素和这个关键点 \(u\) 的LCA \(p\),分讨:

  • 如果栈顶是 \(p\),则可以知道我们加入的关键点 \(u\) 和栈中的点在一条链上,直接将关键点加入栈中即可。如图

  • 如果此时栈顶不是 \(p\)(显然这时候 \(p\) 的 DFS 序比栈顶大,且 栈顶所在位置的子树业已处理完毕),说明 \(p\) 不在链上,将栈顶弹出,直到栈顶为 \(p\),此时插入关键点。
    弹栈的时候记得把他和他父亲的边连一下。

    左子树出栈

酱紫我们的虚树就种好了捏~

Code

bool cmp(int x,int y){return dfn[x]<dfn[y];
}int stk[N],top;
void build(){sort(a+1,a+1+k,cmp);stk[top=1]=1;for(int i=1;i<=k;i++){if(top==1){stk[++top]=a[i];}int lca=LCA(a[i],stk[top]);if(lca==stk[top]) continue;while(top>1 && dfn[stk[top-1]]>=dfs[lca]){addnew(stk[top-1],stk[top]);top--;}if(lca!=stk[top]){addnew(lca,stk[top]);stk[top]=lca;}stk[++top]=a[i];}while(top>0){//别忘了把栈里的连边addnew(stk[top-1],stk[top]);top--;}return;
}

例题

P2495 [SDOI2011] 消耗战

思路

把资源丰富的点看作关键点,建虚树。
对于阳历:

对于询问4 5 7 8 3构建出的虚树

考虑在虚树上DP。

\(f(u)\) 表示切断 \(u\) 的子树中的所有点的代价,\(mn(u)\) 表示从 \(u\) 到根节点的路径上最小的边权

分两种情况

如果 \(u\) 上边有资源,那么不管子树怎么样,\(u\)都要与根节点分离,即\(f(u)=mn(u)\)

否则就是 \(min(mn(u),\sum_{v=son[u]}f(v))\)

Code

这里是用单调栈实现的。

Elaina's Code
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rd read()
#define mkp make_pair
#define psb push_back
#define Elaina 0
#define random(a,b) (1ll*rand()*rand()*rand()%((b)-(a)+1)+(a))
inline int read(){int f=1,x=0;char ch=getchar();for(;!isdigit(ch);ch=getchar()) f=(ch=='-'?-1:1);for(;isdigit(ch);ch=getchar()) x=(x<<3)+(x<<1)+ch-'0';return f*x;
}
const int mod=998244353;
const int N=1e6+100;
const int inf=0x7fffffff7fffffff;int n,m,k,a[N];//原树
int h[N],idx;
struct EDGE{int to,nxt,w;
}e[N];void add(int x,int y,int z){e[++idx]=(EDGE){y,h[x],z};h[x]=idx;
}//虚树
int h1[N],idx1;
struct EDGE1{int to,nxt;
}e1[N];void addnew(int x,int y){e1[++idx1]=(EDGE1){y,h1[x]};h1[x]=idx1;
}int cnt,f[N],dfn[N],siz[N],son[N],topf[N],dep[N];
int mn[N];
void dfs1(int x,int fa){siz[x]=1;f[x]=fa;for(int i=h[x];i;i=e[i].nxt){int to=e[i].to;if(to==fa) continue;dep[to]=dep[x]+1;mn[to]=min(mn[x],e[i].w);dfs1(to,x);siz[x]+=siz[to];if(siz[to]>siz[son[x]]) son[x]=to;}
}void dfs2(int x,int topfa){topf[x]=topfa;dfn[x]=++cnt;if(!son[x]) return ;dfs2(son[x],topfa);for(int i=h[x];i;i=e[i].nxt){int to=e[i].to;if(!topf[to]){dfs2(to,to);}}
}int LCA(int x,int y){//树剖LCAwhile(topf[x]!=topf[y]){if(dep[topf[x]]<dep[topf[y]]) swap(x,y);x=f[topf[x]];}if(dep[x]<dep[y]) swap(x,y);return y;
}bool cmp(int x,int y){return dfn[x]<dfn[y];
}int stk[N],top;
void build(){//建虚树sort(a+1,a+1+k,cmp);stk[top=1]=1;for(int i=1;i<=k;i++){if(top==1){stk[++top]=a[i];}int lca=LCA(a[i],stk[top]);if(lca==stk[top]) continue;while(top>1 && dfn[stk[top-1]]>=dfn[lca]){addnew(stk[top-1],stk[top]);top--;}if(lca!=stk[top]){addnew(lca,stk[top]);stk[top]=lca;}stk[++top]=a[i];}while(top>0){addnew(stk[top-1],stk[top]);top--;}return;
}int dp[N];
int DP(int x){if(h1[x]==0) return mn[x];int ans=0;for(int i=h1[x];i;i=e1[i].nxt){int to=e1[i].to;ans+=DP(to);}h1[x]=0;return dp[x]=min(ans,1ll*mn[x]);
}signed main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);mn[1]=1ll<<60;n=rd;for(int i=1;i<n;i++){int x=rd,y=rd,z=rd;add(x,y,z),add(y,x,z);}dep[1]=1;dfs1(1,0);dfs2(1,1);m=rd;while(m--){k=rd;for(int i=1;i<=k;i++) a[i]=rd;build();printf("%lld\n",DP(1));}return Elaina;
}

就是说该吃饭了对吗? 饿。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

第5周 5.1 顺序与选择结构

5.1 顺序与选择结构 5.1.1 顺序结构 顺序结构是程序中最简单、最基本的流程控制结构,它按照程序中语句出现的先后顺序依次执行,直到程序的结束。 顺序结构示例:public class HelloWorld {public static void main(String[] args) {System.out.println("Hello, World!&q…

基于python的四则运算自动生成的命令行程序

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/CSGrade22-34这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13230这个作业的目标 基于python的四则运算自动生成的命令行程序团队成员 姚彬彬 3122006324一.Github地址 https://g…

WINCCV7.5SP2VBA编程8-通过事件执行脚本

这一篇在新浪博客发表过,审核周期有点长,为了避免丢失,这里再记录一遍。 有三种途径执行Wincc画面设计器的VBA脚本:事件、用户自定义菜单和工具栏、VBA编辑器。 前面的学习是通过VBA编辑器执行的VBA程序,现在通过事件来练习VBA程序执行。 还是在前面WINCC项目程序来做练习…

WinToUSB 9.0 离线注册

WinToUSB 9.0 qt程序,注册验证代码与EasyUEFI 大同小异,这里仅记录相关类、函数地址 关联 https://www.cnblogs.com/DirWang/p/18149030 目录WinToUSB 9.0CActivationDlgCActivationDlg QMetaObject__dCActivationRegisterPageCActivationRegisterPage QMetaObject__dCActiva…

结对项目:自动生成小学四则运算题目

这个作业属于哪个课程 https://edu.cnblogs.com/campus/gdgy/CSGrade22-34这个作业要求在哪里 https://edu.cnblogs.com/campus/gdgy/CSGrade22-34/homework/13230这个作业的目标 结对实现一个自动生成小学四则运算题目的命令行程序项目一、项目开发人员以及仓库地址 1、开发人…

【漏洞分析】20240507-SATURN:当闪电贷遇上有缺陷的通缩机制

背景信息 2024 年 5 月 6 日,SATURN 代币遭受价格操控攻击,损失 15 BNB。攻击发生的原因是由于 SATURN 代币的代币通缩机制设计不合理,使得攻击者可以通过燃烧池子中的 SATURN 代币来操控价格完成获利。项目社媒:https://x.com/Saturn_POM 社媒告警:https://twitter.com/C…

卫生纸国家标准查询 All In One

卫生纸国家标准查询 All In One 强制标准 推荐标准 指导性技术文件卫生纸国家标准查询 All In One国家标准全文公开系统强制标准 推荐标准 指导性技术文件 demos卫生纸 808080序号 标准号 是否采标 标准名称 状态 发布日期 实施日期1 GB/T 20808-2022纸巾 现行 2022-04-15 2023…

ai换脸工具roop 食用教程

1. 准备工作 开源项目地址 https://github.com/s0md3v/roop说明文档 https://docs.facefusion.io/usage/cli-argumentspython环境安装必须是python3.10版本 2 部署 git clone仓库 git clone https://github.com/s0md3v/roop.git2.1 conda创建虚拟环境 conda create -n env_name…