P4211 [LNOI2014]LCA
Problem
给一棵\(n\)个节点的有根树,\(m\)次询问,每次询问给\(l,r,z\),求\(\sum_{i = l}^r dep[LCA(i,z)]\)
\(1 \le n,m \le 500000\)
Solution
Thinking 1
我会暴力!
树剖LCA,\(\mathcal{O}(n\log{n} + nq)\)属于是。
你要树剖LCA不会只会倍增的话那还得加个\(\log{n}\cdot q\)
Thinking 2
我会找性质!
发现对于任意两点\(x,y\),询问\(dep[LCA(x,y)]\)其实就是等同于:
在根节点到\(x\)的链上加1
求\(y\)到根节点的和
那么对于多组询问\(x \in [l,r]\),我们可以把\(l \sim r\)的所有点到根节点都先加1,然后从\(z\)到根跑一遍即得\(\sum_{i = l} ^ r dep[LCA(i,z)]\)。
树剖搞搞就行。
Thinking 3
我是离线人!
把所有询问\(\{l,r,z\}\)拆成\(\{l - 1,z,-1\}\)和\(\{r,z,1\}\)
然后按照首排序。
我们把所有点到根节点顺着加一遍,然后中间询问处理一下。这样复杂度非常优秀!
# include <bits/stdc++.h>
using namespace std;
const int N = 5e4 + 5,mod = 201314;
int n,m;
vector <int> g[N];
int f[N],dep[N];
int dfn[N],siz[N],top[N],son[N],dfntot = 0;
int Ans[N];
struct node
{
int val,lazy;
}T[N << 2];
struct Query
{
int x,z,opt;
int answer,id;
Query() {}
Query(int _x,int _z,int _opt,int _id) : x(_x),z(_z),opt(_opt),id(_id) {}
}Q[N << 1];
bool compare(const struct Query &x, const struct Query &y)
{
return x.x < y.x;
}
int Hash[N][2];
void dfs1(int x)
{
siz[x] = 1;
for(int i = 0; i < (int)g[x].size(); i++)
{
int v = g[x][i];
if(!dep[v])
{
dep[v] = dep[x] + 1;
f[v] = x;
dfs1(v);
siz[x] += siz[v];
if(siz[v] > siz[son[x]]) son[x] = v;
}
}
return;
}
void dfs2(int x,int _top)
{
top[x] = _top;
dfn[x] = ++dfntot;
if(son[x]) dfs2(son[x],_top);
for(int i = 0; i < (int)g[x].size(); i++)
{
int v = g[x][i];
if(v != f[x] && v != son[x])
{
dfs2(v,v);
}
}
return;
}
void pushdown(int root,int l,int r)
{
if(T[root].lazy == 0) return;
int tag = T[root].lazy,mid = (l + r) >> 1;
T[root << 1].val = (T[root << 1].val + tag * (mid - l + 1)) % mod;
T[root << 1 | 1].val = (T[root << 1 | 1].val + tag * (r - mid)) % mod;
T[root << 1].lazy = (T[root << 1].lazy + tag) % mod;
T[root << 1 | 1].lazy = (T[root << 1 | 1].lazy + tag) % mod;
T[root].lazy = 0;
return;
}
void update(int root,int l,int r,int s,int t,int d)
{
if(l <= s && t <= r)
{
T[root].val = (T[root].val + d * (t - s + 1)) % mod;
T[root].lazy = (T[root].lazy + d) % mod;
return;
}
pushdown(root,s,t);
int mid = (s + t) >> 1;
if(l <= mid) update(root << 1,l,r,s,mid,d);
if(r > mid) update(root << 1 | 1,l,r,mid + 1,t,d);
T[root].val = (T[root << 1].val + T[root << 1 | 1].val) % mod;
return;
}
int query(int root,int l,int r,int s,int t)
{
if(l <= s && t <= r) return T[root].val % mod;
pushdown(root,s,t);
int mid = (s + t) >> 1;
int ans = 0;
if(l <= mid) ans = (ans + query(root << 1,l,r,s,mid)) % mod;
if(r > mid) ans = (ans + query(root << 1 | 1,l,r,mid + 1,t)) % mod;
return ans % mod;
}
void modify(int x,int y,int d = 1) // add link(x,y) to 1
{
while(top[x] != top[y])
{
if(dep[top[x]] > dep[top[y]])
swap(x,y);
update(1,dfn[top[y]],dfn[y],1,dfntot,d);
y = f[top[y]];
}
if(dep[x] > dep[y]) swap(x,y);
update(1,dfn[x],dfn[y],1,dfntot,d);
return;
}
int q(int x,int y)
{
int ans = 0;
while(dep[top[x]] != dep[top[y]])
{
if(dep[top[x]] > dep[top[y]])
swap(x,y);
ans = (ans + query(1,dfn[top[y]],dfn[y],1,dfntot)) % mod;
y = f[top[y]];
}
if(dep[x] > dep[y]) swap(x,y);
ans = (ans + query(1,dfn[x],dfn[y],1,dfntot)) % mod;
return ans;
}
int main(void)
{
scanf("%d%d",&n,&m);
for(int i = 2; i <= n; i++)
{
int v; scanf("%d",&v);
++v;
g[i].push_back(v),g[v].push_back(i);
}
dep[1] = 1;
dfs1(1),dfs2(1,1);
for(int i = 1; i <= m; i++)
{
int l,r,z; scanf("%d%d%d",&l,&r,&z); ++l,++r,++z;
Q[i * 2 - 1] = Query(l - 1,z,-1,i);
Q[i * 2] = Query(r,z,1,i);
Hash[l - 1][0] = i * 2 - 1,Hash[r][1] = i * 2;
}
sort(Q + 1, Q + 2 * m + 1, compare);
for(int i = 1,j = 1; i <= 2 * m; i++)
{
while(j <= n && j <= Q[i].x)
{
modify(1,j);
++j;
}
Q[i].answer = q(1,Q[i].z);
// printf("i = %d,x = %d,z = %d,answer = %d\n",i,Q[i].x,Q[i].z,Q[i].answer);
}
for(int i = 1; i <= 2 * m; i++)
{
Ans[Q[i].id] = (Ans[Q[i].id] + Q[i].opt * Q[i].answer + mod) % mod;
}
for(int i = 1; i <= m; i++) printf("%d\n",Ans[i]);
return 0;
}