P4211 [LNOI2014] LCA

题目大意

给出一个 \(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;
}
posted @ 2024-05-19 17:40  tanghg  阅读(18)  评论(0编辑  收藏  举报