• CF1062E 线段树/LCA


    原题大意

    给定一个(n)个点的以(1)为根的子树,定义根的深度为(0).给出(q)个询问,每个询问给定一个区间([l,r])输出在此区间内任意删除一个点之后,剩下的所有的点的(lca)的深度最大值.

    数据范围:

    (1 leq n,q leq 10^5)

    思路

    看到多点求(lca)有个很经典的结论:多个点的lca是这些点中dfn最小的点和最大的点的lca.虽然现在还不知道它有什么用,先放着.

    性质

    • 任意多个点的lca等于dfn最小的点和最大的点的lca
    • 记最小的点是(a),最大的点是(b).首先可以证明,这两个点的(lca)一定是集合内所有点的祖先,假设某个点不是这个点的子孙节点,如果这个点的dfn值在(a,b)之间,那么从根节点往下遍历的时候势必会遍历到这个点,那么这个点一定是夹在中间的,虽然求的lca不一定是根节点,但是一定存在一条根节点从上往下走到这个lca并走到这个点的路径,否则和dfn的定义矛盾了.也就是说除非这个点的dfn是超过这两个点范围的,否则必然存在一个路径到达他,也就必然是他的祖先节点.
    • 其次可以证明不存在比这个点的更靠下的点,也就是他是最近的点,可以假设存在一个点比他的深度更大,那就是更近,那么显然和他是(a,b)的lca这个定义矛盾了,所以这个点也一定是最近的.

    那么这个性质有个很奇特的地方:他可以说明:如果在一段区间里删除一个数,那么如果没有删最左侧最右侧(dfn最小最大的点)的话,是不会对这段区间的点的lca产生影响的,那么接下来可以简单讨论一下:

    • 整个区间只有两个数,此时直接判断选谁的深度比较大就可以了
    • 区间的长度是(3)以上,此时可以删除中间的某个点,则lca保持不变,取深度即可.
    • 删除最小值,那么现在的最小值会由原来的次小值的点替换上,对次小值和最大值求lca即可.
    • 删除最大值,那么现在的最大值会由原来的次大值的点替换上,对次大值和最小值求lca即可.

    那么有关查询最大值最小值,套一个RMQ即可.lca可以用树上倍增或者其他方法求.我用的是线段树+树上倍增,查询一次的复杂度是(O(log^2n)).这个题的数据范围还是比较小的,可以直接过掉.当然也可以极限一点,使用同样预处理是(O(nlogn))的ST表以及树链剖分求lca,就可以把单次查询降到(O(1))了.

    在具体实现的时候,线段树维护最大值最小值,只维护最大值最小值时,不知道对应的点的关系,所以有一个反映射的(rdfn)数组,因为(dfn)本身的取值就是唯一的所以没什么影响,当然更好的做法是在维护线段树的使用使用点而不是值.

    代码

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define forn(i,x,n) for(int i = x;i <= n;++i)	
    
    const int N = 1e5+7,M = 2 * N,LIM = 18,INF = 1e9;
    struct Node
    {
    	int l,r;
    	int mx,mn;
    }tr[N * 4];
    
    int edge[M],succ[M],ver[N],idx;
    int depth[N],fa[N][LIM + 3];
    int dfn[N],time_stamp;
    int rdfn[N];
    
    void add(int u,int v)
    {
    	edge[idx] = v;
    	succ[idx] = ver[u];
    	ver[u] = idx++;
    }
    
    void dfs(int u,int father)
    {
    	dfn[u] = ++time_stamp;rdfn[time_stamp] = u;
    	for(int i = ver[u];~i;i = succ[i])
    	{
    		int v = edge[i];
    		if(v == father)	continue;
    		if(depth[v] > depth[u] + 1)
    		{
    			depth[v] = depth[u] + 1;
    			fa[v][0] = u;
    			for(int k = 1;k <= LIM;++k)
    				fa[v][k] = fa[fa[v][k - 1]][k - 1];
    			dfs(v,u);
    		}
    	}
    }
    
    int lca(int x,int y)
    {
    	if(depth[x] <= depth[y])	swap(x,y);
    	for(int k = LIM;k >= 0;--k)
    		if(depth[fa[x][k]] >= depth[y])
    			x = fa[x][k];
    	if(x == y)	return x;
    	for(int k = LIM;k >= 0;--k)
    		if(fa[x][k] != fa[y][k])
    			x = fa[x][k],y = fa[y][k];
    	return fa[x][0];
    }
    
    void pushup(int u)
    {
    	auto& s = tr[u],&lf = tr[u << 1],&rt = tr[u << 1 | 1];
    	s.mx = max(lf.mx,rt.mx);
    	s.mn = min(lf.mn,rt.mn);
    }
    
    void build(int u,int l,int r)
    {
    	if(l == r)	tr[u] = {l,r,dfn[l],dfn[r]};
    	else
    	{
    		int mid = l + r >> 1;
    		tr[u] = {l,r,-INF,INF};
    		build(u << 1,l,mid),build(u << 1 | 1,mid + 1,r);
    		pushup(u);
    	}
    }
    
    int query_max(int u,int l,int r)
    {
    	if(tr[u].l >= l && tr[u].r <= r)	return tr[u].mx;
    	int mid = tr[u].l + tr[u].r >> 1,res = 0;
    	if(l <= mid)	res = max(res,query_max(u << 1,l,r));
    	if(r > mid)		res = max(res,query_max(u << 1 | 1,l,r));
    	return res;
    }
    
    int query_min(int u,int l,int r)
    {
    	if(tr[u].l >= l && tr[u].r <= r)	return tr[u].mn;
    	int mid = tr[u].l + tr[u].r >> 1,res = INF;
    	if(l <= mid)	res = min(res,query_min(u << 1,l,r));
    	if(r > mid)		res = min(res,query_min(u << 1 | 1,l,r));
    	return res;
    }
    int main()
    {
    	memset(ver,-1,sizeof ver);
    	int n,q;scanf("%d%d",&n,&q);
    	forn(i,2,n)
    	{
    		int p;scanf("%d",&p);
    		add(i,p),add(p,i);
    	}
    	
    	memset(depth,0x3f,sizeof depth);
    	depth[0] = -1,depth[1] = 0;
    	dfs(1,-1);
    	
    	build(1,1,n);
    
    	while(q--)
    	{
    		int l,r;scanf("%d%d",&l,&r);
    		if(r - l + 1 == 2)
    		{
    			if(depth[l] < depth[r])	printf("%d %d
    ",l,depth[r]);
    			else printf("%d %d
    ",r,depth[l]);
    			continue;
    		}
    		int mx = query_max(1,l,r),mn = query_min(1,l,r);
    		int smx = max(query_max(1,l,rdfn[mx] - 1),query_max(1,rdfn[mx] + 1,r));
    		int smn = min(query_min(1,l,rdfn[mn] - 1),query_min(1,rdfn[mn] + 1,r));
    		int L_lca = lca(rdfn[smn],rdfn[mx]),R_lca = lca(rdfn[mn],rdfn[smx]);
    		if(depth[L_lca] > depth[R_lca])	printf("%d %d
    ",rdfn[mn],depth[L_lca]);
    		else printf("%d %d
    ",rdfn[mx],depth[R_lca]);
    	}
    	
        return 0;
    }
    
  • 相关阅读:
    添加arcgis server js API 在aptana环境中的代码帮助
    VS2010安装报错,提示“ 无法打开数据文件deffactory.dat”
    arctoolbox中出现错误:'Invalid Topology [INCOMPLETE_VOID_POLY]的解决办法
    Fiddler2(转)
    ArcGIS 10.X功能增减(转)
    ubuntu 修改root密码
    MongoDb 入门(1) windows 7安装Mongodb
    Pylons 安装
    ubuntu 配置 nginx+pylons [发布应用]
    win7 配置Python quixote Web Framework
  • 原文地址:https://www.cnblogs.com/HotPants/p/14333489.html
Copyright © 2020-2023  润新知