首页 > 其他分享 >虚树总结

虚树总结

时间:2024-08-18 20:07:11浏览次数:4  
标签:总结 limits int 复杂度 uson lca 虚树 dp

之前学了一些算法,没有写算法总结,未来会陆续补一些。


前置知识:树形 \(dp\),\(lca\),\(dfs\) 序。

我们考虑 \([HEOI2014]\) 大工程 这道题。

显而易见,假如这道题只有一次询问,我们可以直接树形 \(dp\),快速求出答案,时间复杂度 \(O(n)\)。

但是,梦想是梦想,现实是现实,这题多组询问,假如一遍一遍求,时间复杂度 \(O(nm)\)。

同时,由于改变的是选择的点,所以你就可以提前放弃 \(up\ and\ down\) 了……

但是这道题有一个很有意思的性质:\(\sum k\le 2n\)。

这也就意味着,假如树的点数只有 \(k\) 个,\(O(\sum k)\) 的时间复杂度是可以 \(AC\) 的,这就很好。

那么,接下来的问题就是:合理转化这棵树,使得,我们可以单次用 \(O(k)\) 或 \(O(k\log k)\) 的时间复杂度建立一棵点数级别为 \(O(k)\) 的虚树(实际上还可以用单调栈建立虚树,但这里讲我比较拿手的 \(lca\) 建立虚树)。


我们考虑一下,一棵虚树中需要维护哪些点。

  1. 显而易见,题目中类似于“选中点”这种的关键点一定要选。

  2. 为了方便,通常我们也会选择树的根节点(便于维护答案,不选也行)。

  3. 其他的点都只是路过,但是两个关键点的共同祖先(即 \(lca\))是一定要选的,因为要通过他转移答案。

那这样很好想到暴力 \(O(k^2)\) 枚举 \(lca\),当然这样时间复杂度绝对会爆炸。

我们考虑下面这种方法:

  1. 对关键点序列 \(a\) 按照 \(dfs\) 序排序。

  2. 将相邻两个关键点的 \(lca\) 插入序列。

  3. 再对序列排一次序。

  4. \(a_i\) 与 \(lca(a_{i-1},a_i)\) 连边。

时间复杂度 \(O(k\log k)\)。


对于本题,设 \(dp_{u,0/1/2}\) 表示第 \(u\) 个节点的子树内,所有选中节点到它的距离之和/选中节点中到它的最短距离/选中节点中到它的最长距离,\(as_{u,0/1/2}\) 则代表对于这个子树,题目所问问题的三个答案,\(i1,i2\) 分别为使 \(dp_{u,1/2}\) 取极值的 \(v\)。

则 \(dp\) 方程为:

\[dp_{u,0}=\sum\limits_{v\in uson}dis(u,v)\times sz_v+dp_{0,v} \]

\[dp_{u,1}=\min\limits_{v\in uson}dp_{v,1}+dis(u,v) \]

\[dp_{u,2}=\max\limits_{v\in uson}dp_{v,2}+dis(u,v) \]

\[as_{u,0}=\sum\limits_{v\in uson}as_{v,0}+(dis(u,v)\times sz_v+dp_{v,0})(sz_x-sz_y) \]

\[as_{u,1}=\min(\min\limits_{v\in uson}as_{v,1},\min\limits_{v\in uson且i1\ne v}dp_{u,1}+dp_{v,1}+dis(u,v)) \]

\[as_{u,2}=\max(\max\limits_{v\in uson}as_{v,2},\max\limits_{v\in uson且i2\ne v}dp_{u,2}+dp_{v,2}+dis(u,v)) \]

时间复杂度 \(O(\sum k\log k)\)。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1000005;
int n,l,q,dfn[N],fa[N][21],p[N],a[N];
int m,r,t,k,h[N],nxt[N*2],to[N*2];
int d[N],nex[N*2],go[N*2],dep[N],b[N*2];
ll dp[3][N],as[3][N],c[N*2],sz[N];
int cmp(int x,int y){
	return dfn[x]<dfn[y];
}void ad(int x,int y){
	to[++m]=y;nxt[m]=h[x];h[x]=m;
}void add(int x,int y,int z){
	go[++r]=y;c[r]=z;
	nex[r]=d[x];d[x]=r;
}void dfs(int x,int f){
	dep[x]=dep[f]+1;
	fa[x][0]=f;dfn[x]=++l;
	for(int i=0;i<20;i++)
		fa[x][i+1]=fa[fa[x][i]][i];
	for(int i=h[x];i;i=nxt[i])
		if(f!=to[i]) dfs(to[i],x);
}int lca(int x,int y){
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=20;~i;i--)
		if(dep[x]-dep[y]>=(1<<i))
			x=fa[x][i];
	if(x==y) return x;
	for(int i=20;~i;i--)
		if(fa[x][i]!=fa[y][i])
			x=fa[x][i],y=fa[y][i];
	return fa[x][0];
}int dis(int x,int y){
	return dep[x]+dep[y]-2*dep[lca(x,y)];
}void dp_(int x,int f){
	ll i1=0,i2=0;
	as[1][x]=1e18;
	if(p[x]==2) sz[x]=1;
	else dp[1][x]=1e18;
	for(int i=d[x];i;i=nex[i]){
		int y=go[i];
		if(y==f) continue;
		dp_(y,x);sz[x]+=sz[y];
		dp[0][x]+=c[i]*sz[y]+dp[0][y];
		if(dp[1][x]>dp[1][y]+c[i])
			dp[1][x]=dp[1][y]+c[i],i1=y;
		if(dp[2][x]<dp[2][y]+c[i])
			dp[2][x]=dp[2][y]+c[i],i2=y;
	}if(p[x]==2) as[2][x]=dp[2][x];
	for(int i=d[x];i;i=nex[i]){
		int y=go[i];if(y==f) continue;
		as[0][x]+=as[0][y]+(c[i]*sz[y]+dp[0][y])*(sz[x]-sz[y]);
		as[1][x]=min(as[1][y],as[1][x]);
		as[2][x]=max(as[2][y],as[2][x]);
		if(i1!=y)
			as[1][x]=min(as[1][x],dp[1][x]+dp[1][y]+c[i]);
		if(i2!=y)
			as[2][x]=max(as[2][x],dp[2][x]+dp[2][y]+c[i]);
	}
}int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n;
	for(int i=1,x,y;i<n;i++)
		cin>>x>>y,ad(x,y),ad(y,x);
	dfs(1,0);
	cin>>q;while(q--){
		cin>>k;
		for(int i=1;i<=k;i++)
			cin>>a[i],b[++t]=a[i],p[a[i]]=2;
		sort(a+1,a+k+1,cmp);
		for(int i=1;i<k;i++){
			int x=lca(a[i],a[i+1]);
			if(!p[x]) p[x]=1,b[++t]=x;
		}sort(b+1,b+t+1,cmp);
		for(int i=1;i<t;i++){
			int lc=lca(b[i],b[i+1]);
			add(lc,b[i+1],dep[b[i+1]]-dep[lc]);
			add(b[i+1],lc,dep[b[i+1]]-dep[lc]);
		}int rt=lca(b[1],b[2]);dp_(rt,0);
		cout<<as[0][rt]<<" "<<as[1][rt]<<" "<<as[2][rt]<<"\n";
		for(int i=1;i<=t;i++){
			p[b[i]]=sz[b[i]]=d[b[i]]=0;
			dp[0][b[i]]=dp[1][b[i]]=dp[2][b[i]]=0;
			as[0][b[i]]=dp[1][b[i]]=as[2][b[i]]=0;
		}for(int i=1;i<=r;i++)
			nex[i]=go[i]=c[i]=0;
		r=t=0;
	}return 0;
}

标签:总结,limits,int,复杂度,uson,lca,虚树,dp
From: https://www.cnblogs.com/chang-an-22-lyh/p/18365995/xushu-zj

相关文章

  • 第七周总结
    深入并发编程鉴于并发编程在现代软件开发中的重要性,本周我投入了大量时间深入学习了Java的并发编程模型。除了复习之前学过的线程基础、同步机制(如synchronized、volatile、wait/notify)外,我还重点学习了Java并发包(java.util.concurrent)中的高级并发工具,如ExecutorService、Futu......
  • 2024.8.11至2024.8.17周总结
    本周学习任务清单1.字符串:Hash、KMP、trie树、拓展KMP(Z函数)、AC自动机、Manacher、回文自动机、后缀数组、后缀自动机、广义后缀自动机2.数论:欧拉函数、莫比乌斯函数、欧拉反演、莫比乌斯反演、筛法、杜教筛、min25筛3.博弈论:公平组合游戏、反常游戏、SG函数总结本周学习的......
  • 2024暑假总结3
    前言因为现在我开始每天写随笔,所以总结里就不赘述每天的具体的内容和每天的小总结了,因为会给人一种重复感,所以我决定在总结中主要分析我认为非常有价值的地方。考试我认为考试能反映出一个人的很多问题。然后谈一谈8.11的考试。总体来说,题应该不算太难,T1是一个思考难度不大的......
  • 8.17日二分测试总结
    8.17日二分测试总结比赛传送门分数情况A.砍树B.买木头C.数列分段2D.吃冰棍E.跳石头F.奶牛晒衣服10080100\(_{没做:(}\)100总体分数\(_{很惨}\)T1.P1873[COCI2011/2012#5]EKO/砍树题目传送门问题分析运用二分答案与check函数check函数......
  • 中间件漏洞原理与复现大全【万字总结】
    文章目录IISHTTP.SYS远程代码执行漏洞(CVE-2015-1635)漏洞描述影响版本漏洞复现IIS短文件之目录扫描1、概念3、应用场景:4、漏洞利用:IIS文件解析漏洞IIS6解析漏洞IIS7解析漏洞IIS写权限漏洞简介条件漏洞复现NginxNginx文件名逻辑漏洞原理条件复现Nginx解......
  • 近期模拟赛总结
    7/5rnk8,总体不错,仍有进步空间。比赛历程记录个人认为这次的答题策略很优,值得以后学习:T1想了十几分钟,一开始想的有点偏,打了个实测60pts的东西上去,时间过去将近1h;看T2,像是一个计数DP之类的东西,不会,打了30pts的暴力,时间过去1.5h多;看T3,不会;看T4,想到了去年普及组......
  • 《软件测试》黑书全22章笔记总结——软测新手小白必读
    一、软件测试综述1.第一章:软件测试的背景1.1软件缺陷只有至少满足下列5个规则之一才称为发生了一个软件缺陷软件未实现产品说明书要求的功能软件出现了产品说明书指明不应该出现的错误软件实现了产品说明书未提到的功能软件未实现产品说明书虽未明确提及但应该实现的......
  • Transformer问题总结及实现
    目录前提:注意:以下对于优化的问题,要回答这个问题:前一种方法的局限性在哪里,优化的方法是怎么进行优化的?(未完全解决)Step1:关于Transformer的疑问Step2:关于Transformer各层的实现(未解决)2.1:Encoder细节2.2:Decoder细节2.3:怎么用Transformer提升Kaggle平台的House_pricing竞赛?......
  • 原生微信小程序笔记完整总结4.0
       ......
  • 【面试宝典】java基础面试题总结[上]
    一、Java中有几种基本数据类型?各占多少字节?在Java中基本数据类型有8个,占用的字节分别是整型byte(1个字节)、short(2个字节)、int(4个字节)、long(8个字节);浮点型float(4个字节)、double(8个字节);布尔类型boolean;字符类型char(2个字节)。二、String类能被继承吗?为什么?Stri......