首页 > 其他分享 >树上莫队小技巧

树上莫队小技巧

时间:2023-10-14 10:12:49浏览次数:33  
标签:技巧 int 路径 ++ 树上 莫队 id

前言

联考有一道树上莫队一眼题,但是我还没学过树上莫队啊!!!

于是开始口胡,这个东西好像是说这个东西把树拍成欧拉序,端点一移动,做完了!开始写,一下子过大样例,没有细节!

然后在网上一看树上莫队的博客:大家怎么都求了 LCA?为什么要分讨有祖先后代关系的情况?坏了,一定是我做法假了!!!

然后往 SPOJ Count on a tree 2 一交,怎么 T 了?哦,原来是对询问 sort 后才预处理了分块数组,小问题。再一交,欸,过了!

正经的

其实欧拉序树上莫队有一种细节非常少的写法,我们为什么要利用欧拉环游序呢?因为它满足一个非常优秀的性质:相邻两个数在原树上一定相邻。

而莫队让我们做什么?区间左端点右端点变化一。树上莫队能做什么?路径两端点变到相邻的节点。

所以说我们想做路径询问,维护两个指针 \(u,v\) 表示路径两端点,再用一种数据结构存储路径信息,我们只需要保证任意时刻这个数据结构中存储的正好是 \(u,v\) 之间路径的信息就行了。

所以我们树上莫队时对于询问 \(x,y\) 在欧拉序上随便找两个值为 \(x/y\) 的位置 \(l,r\),就把路径询问当成普通莫队中的一个区间。

移动指针时,每次我们只会把 \(u,v\) 移动到相邻节点,那么路径变化是 \(O(1)\) 的:存储一个表示每个点是否在路径上的 bool 数组,每次移动的时候判一下移动到的节点有没有访问来决定是插入还是删除就可以了。

#include <cmath>
#include <cstdio>
#include <bitset>
#include <algorithm>
using namespace std;
int read(){/*...*/}
const int N=100003,M=200003;
int hd[N],ver[M],nxt[M],tot;
void add(int u,int v){nxt[++tot]=hd[u];hd[u]=tot;ver[tot]=v;}
int n,q,bl,cnt;
int pos[N],num,id[M],a[N],bc[N];
void dfs(int u,int fa){
	id[pos[u]=++num]=u;
	for(int i=hd[u];i;i=nxt[i]){
		int v=ver[i];
		if(v==fa) continue;
		dfs(v,u);
		id[pos[u]=++num]=u;
	}
}
bool vis[N];
int res[M];
void mov(int u,int v){
	if(vis[v]){if(!--bc[a[u]]) --cnt;vis[u]=0;}
	else{if(!bc[a[v]]++) ++cnt;vis[v]=1;}
}
int bel[M],buc[M],rk;
struct qry{
	int l,r,x;
	friend bool operator<(const qry x,const qry y){
		int tx=bel[x.l],ty=bel[y.l];
		if(tx!=ty) return tx<ty;
		if(tx&1) return x.r>y.r;
		else return x.r<y.r;
	}
}s[M];
int main(){
	n=read();q=read();
	for(int i=1;i<=n;++i) buc[++rk]=a[i]=read();
	sort(buc+1,buc+rk+1);
	rk=unique(buc+1,buc+rk+1)-buc-1;
	for(int i=1;i<=n;++i) a[i]=lower_bound(buc+1,buc+rk+1,a[i])-buc;
	for(int i=1;i<n;++i){
		int u=read(),v=read();
		add(u,v);add(v,u);
	}
	dfs(1,0);
	for(int i=1;i<=q;++i){
		s[i].l=pos[read()];s[i].r=pos[read()];s[i].x=i;
		if(s[i].l>s[i].r) swap(s[i].l,s[i].r);
	}
	bl=ceil((double)num/sqrt(q));
	for(int i=1;i<=num;++i) bel[i]=(i-1)/bl+1;
	sort(s+1,s+q+1);
	int cl=1,cr=1;
	++bc[a[id[1]]];cnt=1;vis[id[1]]=1;
	for(int i=1;i<=q;++i){
		while(cr<s[i].r) mov(id[cr],id[cr+1]),++cr;
		while(cl>s[i].l) mov(id[cl],id[cl-1]),--cl;
		while(cr>s[i].r) mov(id[cr],id[cr-1]),--cr;
		while(cl<s[i].l) mov(id[cl],id[cl+1]),++cl;
		res[s[i].x]=cnt;
	}
	for(int i=1;i<=q;++i) printf("%d\n",res[i]);
	return 0;
}

标签:技巧,int,路径,++,树上,莫队,id
From: https://www.cnblogs.com/yyyyxh/p/mo_queue_on_tree.html

相关文章

  • P9233 [蓝桥杯 2023 省 A] 颜色平衡树 (dfs序 莫队)
    P9233[蓝桥杯2023省A]颜色平衡树(dfs序莫队)莫队原理:https://zhuanlan.zhihu.com/p/115243708对于树上的每个结点,按照dfs序打上时间戳,这样就可以把每一个结点对应的子树的答案转化为一个区间的答案。将子树询问离线下来变成\(n\)个区间查询。用\(cnt\)数组维护颜色......
  • 项目经理涨薪秘籍!技巧都在这里了
    好奇前辈们是如何带好团队、做出成功项目,从而升职加薪,成为高级项目经理或项目管理主管的?这是绝大多数新手PM最关注的事情。今天小编给大家揭秘!一、刚入门如何进阶从入门的项目管理者发展到中级的项目管理者,重点提升的能力包括基础专业能力、工具应用能力、资源整合能力、目标整合能......
  • 一个需要感性理解的树上算法 学习心得
    题目描述你现在有一颗\(n\)个点的树和\(m\)条由\(x_i\)到\(y_i\)(\(1\lex_i\,\y_i\len\))的简单可重复路径。求有多少种方案选路径,使路径集的大小为\(k\),且所有路径至少有一个公共点。对\(10^9+7\)取模。题解这道题需要感性理解做法。我们记一个路径左右......
  • 渗透测试高级技巧(二):对抗前端动态密钥与非对称加密防护
    在前文的技术分享中,我们描述了验签和静态对称加密(静态密钥AES)的常见场景,大家我相信遇到类似的加解密清醒,基本都可以通过热加载的基本使用获得破解前端加密解密的方法,达到一个比较好的测试状态。在本文中,我们在保持同样的通用适配度的同时,将会来接触更加复杂的前端加密与解密场......
  • 【离线算法】- 莫队
    莫队简介莫队是可以支持多次询问区间\([l,r]\)的信息的离线算法。通过将询问范围以块长为\(\sqrtn\)分块后按端点所属分块排序的方式优化复杂度。普通莫队定义普通莫队针对的是序列上的区间询问。常见形式为:对于一个长度为\(n\)的序列,提出\(m\)次询问,每次询问区间......
  • 树上莫队
    20231012树上莫队由于联考考到,又直接爆0,于是来学习。树上莫队——把莫队放到树上。但我是真的不知道把莫队怎么放到树上。。。于是我们考虑一个东西叫做欧拉序,就是再dfs的时候在进栈和出栈的地方都记录一下。而在区间查询的时候,我们只对区间出现一次的数统计答案,用一个......
  • 树上的最大权连通块:一种换根动态规划与贪心算法的结合
    树上的最大权连通块:一种换根动态规划与贪心算法的结合在计算机科学中,树是一种非常特殊的数据结构,不仅因为它们在存储数据时的效率,还因为它们提供了一种非常直观且强大的方式来解决各种问题。今天,我们将探讨一种特殊类型的问题,即在一棵树中找到一个特殊的子集或连通块,该子集中的节......
  • 【前端小技巧】如何使用 Eolink Apilkit 调用 Mock ?
    在开发过程中,进度比较赶的情况下,前端人员当页面写完时,后台的接口还没写完,等要交付的时候后端才把接口给你,这个时候就很尴尬。这个时候Mock就可以很好的解决这个问题,前端团队可以在API还没开发完成的情况下,借助MockAPI实现预对接,加速开发进程。测试团队可以通过MockAPI解......
  • 【前端小技巧】如何使用 Eolink Apilkit 调用 Mock ?
    在开发过程中,进度比较赶的情况下,前端人员当页面写完时,后台的接口还没写完,等要交付的时候后端才把接口给你,这个时候就很尴尬。这个时候Mock就可以很好的解决这个问题,前端团队可以在API还没开发完成的情况下,借助MockAPI实现预对接,加速开发进程。测试团队可以通过MockAPI......
  • 6577: 暗的连锁 LCA+树上差分
    描述  Dark是一张无向图,图中有N个节点和两类边,一类边被称为主要边,而另一类被称为附加边。Dark有N–1条主要边,并且Dark的任意两个节点之间都存在一条只由主要边构成的路径。另外,Dark还有M条附加边。你的任务是把Dark斩为不连通的两部分。一开始Dark的附加边......