题目大意
给出一个 \(n\) 个节点的有根树。
设 \(dep[i]\) 表示点 \(i\) 的深度,\(\operatorname{LCA}(i, j)\) 表示 \(i\) 与 \(j\) 的
最近公共祖先。
有 \(m\) 次询问,每次询问给出 \(l, r, z\),求 \(\sum_{i=l}^r dep[\operatorname{LCA}(i,z)]\) 。
\(1\le n,m\le 50000\)。
思路
对于这种题目,由于最近公共祖先是不断变化的,所以在 \(dep\) 上直接做两点的容斥不是一个明智的思路。
并且显然这个 \(\sum_{i=l}^r\) 不好优化,所以考虑预处理出所有的答案之后用类似差分的方法解决。即 \(\sum_{i=l}^r ? = \sum_{i=1}^r ?-\sum_{i=1}^{l-1} ?\)。具体的我们可以通过遍历 \(1\) 到 \(n\) 然后预处理出每一个位置上有哪些询问的 \(z\) 在这个上面,之后处理。所以说我们现在的复杂度是 \(O(n+q\cdot \alpha)\) 的,\(\alpha\) 指求出 \(\sum_1^i dep[LCA(i,z)]\) 的复杂度。
考虑最近公共祖先的定义。两个点的最近公共祖先往上直到根都是两个点的祖先。 即这个公共部分包括多少个节点就是指这个最近公共祖先的深度。那我们预处理一下,对于当前考虑的点 \(i\),将 \(i\) 到根的点权全部加一。对于每一个 \(z\) 的查询我们只需要查询 \(z\) 到根上所有节点的点权之和就是答案。因为这一定是某个节点与它的公共部分且为最长,否则路径上一定还有点也在 \(z\) 到根的路径当中。使用树链剖分解决。
最终时间复杂度 \(O(n\log^2 n)\)。
代码
#include<bits/stdc++.h>
#define lc(u) u<<1
#define rc(u) u<<1|1
#define int long long
using namespace std;
typedef long long ll;
const ll MAXN=5e4+5;
ll t[MAXN*8],tag[MAXN*8];
void push_up(ll u){
t[u]=t[lc(u)]+t[rc(u)];
}
void push_down(ll u,ll l,ll r){
ll mid=(l+r)>>1;
t[lc(u)]+=tag[u]*(mid-l+1);
t[rc(u)]+=tag[u]*(r-mid);
tag[lc(u)]+=tag[u];
tag[rc(u)]+=tag[u];
tag[u]=0;
}
void add(ll u,ll l,ll r,ll ql,ll qr){
if(ql<=l&&r<=qr){
t[u]+=(r-l+1);
tag[u]++;
return;
}
push_down(u,l,r);
ll mid=(l+r)>>1;
if(ql<=mid){
add(lc(u),l,mid,ql,qr);
}
if(mid+1<=qr){
add(rc(u),mid+1,r,ql,qr);
}
push_up(u);
}
ll query(ll u,ll l,ll r,ll ql,ll qr){
if(ql<=l&&r<=qr){
return t[u];
}
push_down(u,l,r);
ll mid=(l+r)>>1,ans=0;
if(ql<=mid){
ans+=query(lc(u),l,mid,ql,qr);
}
if(mid+1<=qr){
ans+=query(rc(u),mid+1,r,ql,qr);
}
return ans;
}
vector<ll>adj[MAXN];
ll fa[MAXN],dep[MAXN],hs[MAXN],sz[MAXN];
ll dfn[MAXN],id[MAXN],cs[MAXN],node_id;
ll n,m;
void dfs1(ll u){
sz[u]=1;
for(auto v:adj[u]){
dfs1(v);
sz[u]+=sz[v];
dep[v]=dep[u]+1;
if(!hs[u]||sz[hs[u]]<sz[v]){
hs[u]=v;
}
}
}
void dfs2(ll u,ll start){
cs[u]=start;
dfn[++node_id]=u;
id[u]=node_id;
if(hs[u]){
dfs2(hs[u],start);
}
for(auto v:adj[u]){
if(v==hs[u]){
continue;
}
dfs2(v,v);
}
}
void modify(ll u){
while(cs[u]!=1){
add(1,1,n,id[cs[u]],id[u]);
u=fa[cs[u]];
}
add(1,1,n,1,id[u]);
}
ll val(ll u){
ll aa=0;
while(cs[u]!=1){
aa+=query(1,1,n,id[cs[u]],id[u]);
u=fa[cs[u]];
}
aa+=query(1,1,n,1,id[u]);
return aa;
}
ll l[MAXN],r[MAXN],z[MAXN];
bool vis[MAXN];
map<pair<ll,ll>,ll>ma;
vector<ll>pool[MAXN];
signed main(){
cin>>n>>m;
for(int i=2;i<=n;++i){
ll ff;
cin>>fa[i];
++fa[i];
adj[fa[i]].push_back(i);
}
dfs1(1);
dfs2(1,1);
for(int i=1;i<=m;++i){
cin>>l[i]>>r[i]>>z[i];
++l[i];
++r[i];
++z[i];
pool[l[i]-1].push_back(z[i]);
pool[r[i]].push_back(z[i]);
}
for(int i=1;i<=n;++i){
modify(i);
for(auto v:pool[i]){
ma[{i,v}]=val(v);
}
}
for(int i=1;i<=m;++i){
cout<<(ma[{r[i],z[i]}]-ma[{l[i]-1,z[i]}])%201314<<endl;
}
return 0;
}
标签:dep,ll,LNOI2014,tag,MAXN,P4211,LCA,sum
From: https://www.cnblogs.com/tanghg/p/18200534/solution-p4211