首页 > 其他分享 >P4211 [LNOI2014] LCA

P4211 [LNOI2014] LCA

时间:2024-05-19 17:53:16浏览次数:22  
标签:dep ll LNOI2014 tag MAXN P4211 LCA sum

题目大意

给出一个 \(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

相关文章

  • LCA(最近公共祖先)应用
    LCA可以将一条树上路径拆成一或两半,所以我们可以将很多关于区间的算法拓展到树上。仓鼠找suger洛谷P3398考虑两条相交的纵向路径\([A,B]\)和\([C,D]\),如图:如果两条路径相交那么\(C\)是\(B\)的祖先,\(A\)是\(D\)的祖先,对于任意的路径\([A,X,B]\)和\([C,Y,D]\),如......
  • 蛇形变量名(nake_case)速转驼峰变量名(camelCase)__Java
    最近遇到当JavaBean不遵循驼峰命名规则时,使用反射赋值失败。但是我的类中属性个数非常多(一个一个改也太恼火了),因此写了个将蛇形变量名转驼峰变量名的方法,在此分享出来供大家使用。publicstaticvoidconvertToCamelCase(Objectobj){Class<?>clazz=obj.getClas......
  • dotnet 已知问题 错误标记 MethodImplOptions.InternalCall 特性参数将会在类型访问之
    本文将记录一个dotnet的已知问题。当自己不小心在方法上不正确标记了MethodImplAttribute特性时,错误选择了MethodImplOptions.InternalCall参数,那将会在运行的过程在,在此类型被访问之前就抛出了System.TypeLoadException异常,错误信息是Internalcallmethodwithnon_NUL......
  • 树链剖分lca笔记
    树链剖分求lca参考资料:https://www.cnblogs.com/cangT-Tlan/p/8846408.html[树链剖分lca]大佬讲的很清楚%%%orz这篇博客是我的学习笔记。树链剖分:树链剖分,计算机术语,指一种对树进行划分的算法,它先通过轻重边剖分将树分为多条链,保证每个点属于且只属于一条链,然后再通过数据结......
  • P4211 LCA 题解
    前置知识:树剖、差分题意给定一个\(n\)个节点的有根树树,根为\(1\)。有\(m\)个询问,每个询问给定\(l,r,z\),求\(\sum\limits_{i=l}^rdep[\textrm{LCA}(i,z)]\)。其中\(dep[x]\)表示\(x\)的深度,有\(dep[1]=1\)。题解式子中的\(dep\)不太好算,考虑转化成形式化......
  • [学习笔记] LCA - 图论
    [NOIP2013提高组]货车运输最大生成树+LCA+倍增好家伙,这道题我写了一个晚上,调了两个晚上,对于这道题我颇有感触。但这道题确实好,实实在在的蓝题,让我发现了许多关于LCA的问题。首先,这个题给的是一个无向图,并不是个树,为了减少运算量,我们可以把它变成一个树。运用Kruskal算法生......
  • LCA 算法 : dfn 序
    洛谷题解区那个题解马蜂让我读到自闭,这篇文章,详细的讲一讲这个算法。一种基于预处理的快速LCA算法。预处理需要\(O(n\logn)\)查询,\(O(1)\),空间复杂度\(O(n\logn)\)。根据dfn序的性质,若\(u\)是\(v\)的祖先,那么$dfn_u<dfn_v$,因为先遍历节点再遍历子树,所以\(u\)......
  • 倍增(LCA与ST表)附详细讲解博客路劲以及洛谷模板题
    前置知识--倍增倍增算法,顾名思义,就是不断地翻倍。虽然是一种基础算法,但它能够使得线性的处理转化为对数级的处理,大大地优化时间复杂度,在很多算法中都有应用,其中最常见的就是ST表以及LCA(树上最近公共祖先)了。学习博客:算法学习笔记(12):ST表-知乎(zhihu.com)for(intx=......
  • LCA + 树上倍增
    LCA+树上倍增一、例题引入题目:2846.边权重均等查询现有一棵由n个节点组成的无向树,节点按从0到n-1编号。给你一个整数n和一个长度为n-1的二维整数数组edges,其中edges[i]=[ui,vi,wi]表示树中存在一条位于节点ui和节点vi之间、权重为wi的边。另......
  • dfs 序求 LCA!
    前言为什么用dfs序求LCA而不用欧拉序?帅常数小,也就一半好玩反正没什么正经理由。正文定义dfs序是指对树进行深度优先遍历后得到的节点序列。\(\mathit{dfn}_i\)是节点\(i\)在dfs序中的位置(从\(0\)或\(1\)开始无影响)。LCA是最近公共祖先。深度\(\ma......