zoukankan      html  css  js  c++  java
  • 【百度之星初赛A】路径交 LCA+线段树

    【百度之星初赛A】路径交

    Problem Description

    给定一棵n个点的树,以及m条路径,每次询问第L条到第R条路径的交集部分的长度(如果一条边同时出现在2条路径上,那么它属于路径的交集)。

    Input

    第一行一个数n(n<=500,000)

    接下来n-1行,每行三个数x,y,z,表示一条从x到y并且长度为z的边 第n+1行一个数m(m<=500,000)

    接下来m行,每行两个数u,v,表示一条从u到v的路径

    接下来一行一个数Q,表示询问次数(Q<=500,000)

    接下来Q行,每行两个数L和R

    Output

    Q行,每行一个数表示答案。

    Sample Input

    4
    1 2 5
    2 3 2
    1 4 3
    2
    1 2
    3 4
    1
    1 2
    

    Sample Output

    5

    题解:看到题的第一反应就是线段树,然后在区间合并的时候求一下两个路径的交即可,现在我们只需要快速求出两个路径的交即可。

    设两条路径分别为a1-b1,a2-b2,它们的lca是c1,c2,求出a1,b1-a2,b2两两之间的lca,设分别为d1,d2,d3,d4,假设dep[d1]<=dep[d2]<=dep[d3]<=dep[d4],dep[c1]<=dep[c2]。那么如果两条路径有交,当且仅当dep[d1]>=dep[c1]且dep[d4]>=dep[d3]>=dep[c2](自己画画就知道了)。并且如果有交,那么交一定是d3-d4。

    如果用RMQ求LCA,则时间复杂度为nlogn。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    #define lson x<<1
    #define rson x<<1|1
    using namespace std;
    const int maxn=500010;
    typedef long long ll;
    struct path
    {
    	int a,b,c;
    	path(){}
    	path(int A,int B,int C)	{a=A,b=B,c=C;}
    }p[maxn],s[maxn<<2];
    int n,m,q,cnt;
    int mn[20][maxn<<1],Log[maxn<<1],pos[maxn],dep[maxn],fa[maxn];
    int head[maxn],nxt[maxn<<1],val[maxn<<1],to[maxn<<1];
    int cs[10];
    ll len[maxn];
    int MN(int a,int b)
    {
    	return dep[a]<dep[b]?a:b;
    }
    int lca(int a,int b)
    {
    	int x=pos[a],y=pos[b];
    	if(x>y)	swap(x,y);
    	int k=Log[y-x+1];
    	return MN(mn[k][x],mn[k][y-(1<<k)+1]);
    }
    bool cmp(int a,int b)
    {
    	return dep[a]<dep[b];
    }
    path mix(path x,path y)
    {
    	if(!x.c||!y.c)	return path(0,0,0);
    	cs[1]=lca(x.a,y.a),cs[2]=lca(x.a,y.b),cs[3]=lca(x.b,y.a),cs[4]=lca(x.b,y.b);
    	sort(cs+1,cs+5,cmp);
    	int md=max(dep[x.c],dep[y.c]),nd=min(dep[x.c],dep[y.c]);
    	if(dep[cs[1]]<nd||dep[cs[3]]<md)	return path(0,0,0);
    	else	return path(cs[3],cs[4],lca(cs[3],cs[4]));
    }
    inline int rd()
    {
    	int ret=0,f=1;	char gc=getchar();
    	while(gc<'0'||gc>'9')	{if(gc=='-')f=-f;	gc=getchar();}
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret*f;
    }
    void add(int a,int b,int c)
    {
    	to[cnt]=b,val[cnt]=c,nxt[cnt]=head[a],head[a]=cnt++;
    }
    void dfs(int x)
    {
    	pos[x]=++pos[0],mn[0][pos[0]]=x;
    	for(int i=head[x];i!=-1;i=nxt[i])
    	{
    		if(to[i]!=fa[x])
    		{
    			fa[to[i]]=x,dep[to[i]]=dep[x]+1,len[to[i]]=len[x]+val[i],dfs(to[i]);
    			mn[0][++pos[0]]=x;
    		}
    	}
    }
    void build(int l,int r,int x)
    {
    	if(l==r)
    	{
    		s[x]=p[l];
    		return ;
    	}
    	int mid=l+r>>1;
    	build(l,mid,lson),build(mid+1,r,rson);
    	s[x]=mix(s[lson],s[rson]);
    }
    path query(int l,int r,int x,int a,int b)
    {
    	if(a<=l&&r<=b)	return s[x];
    	int mid=l+r>>1;
    	if(b<=mid)	return query(l,mid,lson,a,b);
    	if(a>mid)	return query(mid+1,r,rson,a,b);
    	return mix(query(l,mid,lson,a,b),query(mid+1,r,rson,a,b));
    }
    int main()
    {
    	n=rd();
    	int i,j,a,b,c;
    	memset(head,-1,sizeof(head));
    	for(i=1;i<n;i++)	a=rd(),b=rd(),c=rd(),add(a,b,c),add(b,a,c);
    	dep[1]=1,dfs(1);
    	for(i=2;i<=2*n-1;i++)	Log[i]=Log[i>>1]+1;
    	for(j=1;(1<<j)<=2*n-1;j++)	for(i=1;i+(1<<j)-1<=2*n-1;i++)	mn[j][i]=MN(mn[j-1][i],mn[j-1][i+(1<<j-1)]);
    	m=rd();
    	for(i=1;i<=m;i++)	p[i].a=rd(),p[i].b=rd(),p[i].c=lca(p[i].a,p[i].b);
    	build(1,m,1);
    	q=rd();
    	for(i=1;i<=q;i++)
    	{
    		a=rd(),b=rd();
    		path ans=query(1,m,1,a,b);
    		printf("%lld
    ",len[ans.a]+len[ans.b]-2*len[ans.c]);
    	}
    	return 0;
    }
  • 相关阅读:
    LeetCode(122. 买卖股票的最佳时机 II)
    LeetCode(485. 最大连续1的个数)

    约瑟夫问题
    链表
    队列
    稀疏数组
    乐优商城项目学习
    LeetCode_Climbing Stairs
    数据结构&&算法基础知识
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7350785.html
Copyright © 2011-2022 走看看