传送门
普通莫队是对一段一维的序列上操作的算法,即使带修莫队也是增加一个时间轴修改的序列也是一维的,如果对于一个树能否操作呢?可以将对树处理成一个欧拉序来实现,欧拉序是在dfs序的基础上在绕回每个点时将这个点填进去的序列。举个例子:
dfs序:12345678
欧拉序:1233445526778861
定义:s[i]表示节点i入栈的时间戳,t[i]表示节点i出栈的时间戳。
所以欧拉序的长度是2n的,对树上两点u和v之间的路径有两种情况(假设u的深度比v小):
u是v的祖先:那么s[u]到s[v]之间的点(出现两次的点除外)都在u到v之间的路径上,u=1,v=5时就对应欧拉序上区间[1,7]。
u不是v的祖先,这种情况下s[u]到s[v]之间欧拉序u会出现两次,所以要用t[u]到s[v],但是这样的话u和v的lca没有出现在欧拉序内,所以移动区间时还要将lca加进去,记得统计过后在把lca的贡献去掉。
这样就能将树上的路径查询转变为欧拉序的区间查询,套莫队就完了。
欧拉序dfs就可以,求lca可以树剖,欧拉序顺带就出来了。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<unordered_map>
#include<cmath>
using namespace std;
typedef long long ll;
const int N=100010;
int n,m,a[N],nums[N];
int h[N],ne[N],e[N],idx;
void add(int a,int b)
{
e[idx]=b;ne[idx]=h[a];h[a]=idx++;
}
int dep[N],sz[N],son[N],fa[N],dfn[N],top[N],cnt;
int oula[N],s[N],t[N];
void dfs1(int u,int p)
{
sz[u]=1;
oula[++cnt]=u;
s[u]=cnt;
for(int i=h[u];~i;i=ne[i])
{
int v=e[i];
if(v==p) continue;
dep