牛客练习赛39 E 车站(level 4)(线段树+倍增+LCA(ST表))

13 篇文章 0 订阅
3 篇文章 0 订阅

题目链接

题意:

给你一颗树n个点,n-1条边,然后有m条铁路,从1-m标号,第i条铁路是ui-vi的简单路径

一个点可以作为区间[L,R]铁路的车站满足以下条件:    

1、编号为[L,R]的铁路都经过这个车站。    2、编号为[L,R]的铁路经过的所有城市中,离车站最远的城市,

与它的距离最小。如果有多个,那么选择编号较小的。 

然后有两种操作

操作1:1,l,r,表示询问[l,r]铁路的车站。

操作2:2,x,u,v,表示第x条铁路变成从u到v的一条简单路径。

 

解析:

这道题....一开始不小心瞄了一眼题解,看见是用线段树+倍增做的,所以只能试着做一下。

但是知道怎么做,依旧做不出来...

所以直接看了完整的题解


线段树+倍增+LCA。
首先车站一定在所有铁路的经过的点的交集上,所以可以用线段树求出区间路径的交集,同时维护离路径交的两个端点最远的点。找到出了最远的两个点后,那么可以倍增求出车站的位置。
具体地,线段树每个节点维护u,v,du,dv,u,v是路径交,du,dv是分别是距离u,v最远的点。合并两个区间的过程是先对两个区间的路径求路径交,然后新的du,dv一定是两个区间中四个du,dv中的两个。
对一段区间询问,先求出区间的u,v,du,dv,于是可以选择路径du->dv上中间的位置作为车站,如果中间点不在路径交集上,那么让u,v中的一个作为车站。
复杂度O(nlogn)

首先这里的LCA是需要用ST表做的,用倍增在线查询的O(logn)的复杂度太慢了,我一开始这样做,就一直在T

LCA(ST)1

LCA(ST)2

这道题核心的部分其实就是用线段树来维护一个数据结构(这里是维护u->v的路径的端点,以及分别距离u,v最远的点du,dv)

这里可以用线段树维护的两个原因是:1.这道题求的是区间问题[L,R],可以在线查询

2.这个数据结构可以合并,两个数据结构可以合并成一个更大的数据结构(两条路径合并)

最后用[L,R]这区间的数据结构合并成的大数据结构得出答案。

所以关键的部分就是合并——push_up()
push_up()按照题解里一步步写就可以了,这里我有一个地方,想到了但写不出来,就是怎么求u1->v1,u2->v2这两条路径的路径交。

对于一个点y,它在u1->rt1的路径上的充要条件是LCA(y,rt1)==rt1&&LCA(y,u1)==y

那么在u1-v1路径上的充要条件就是(LCA(y,rt1)==rt1&&LCA(y,u1)==y) || (LCA(y,rt1)==rt1&&LCA(y,v1)==y)

那么这个就可以用来就路径交。

题解用的就是分别用u1-u2,u1-v2,v1-u2,v1-v2求4个LCA,然后深度最大的两个LCA就是路径交的两个端点(这个是保证两条路径有交集的情况下的)....这个我也不知道为什么....但是用样例举例又是对的。

 

剩下就没什么难点了,只是在求du-dv的中点的时候,要小心长短的问题

#include <string>
#include <cstring>
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <map>
#include <vector>
#define lch (root<<1)
#define rch ((root<<1)|1)
 
using namespace std;
 
int LCA(int u,int v);
 
const int N = 2e5+10;
 
typedef struct node
{
    int u,v;
    int du,dv;
}node;
 
node Btree[N*4];
 
vector<int> ee[N];
int max0=23;
int dep[N];
node stu[N];
 
int cal_dis(int x,int y)
{
    int r=LCA(x,y);
    return dep[x]-dep[r]+dep[y]-dep[r];
}
 
bool cmp(int x,int y)
{
    return dep[x]<dep[y];
}
 
bool inchain(int x,int y,int z)
{
    int rt=LCA(x,y);
    if(LCA(rt,z)==rt&&LCA(z,x)==z) return true;
    if(LCA(rt,z)==rt&&LCA(z,y)==z) return true;
    return false;
}
 
inline node push_up(node lno,node rno)
{
    node rtno;
    rtno.u=rtno.v=-1;
    if(lno.u==-1||rno.u==-1||lno.v==-1||rno.v==-1) return rtno;
    if(!inchain(lno.u,lno.v,LCA(rno.u,rno.v))&&!inchain(rno.u,rno.v,LCA(lno.u,lno.v))) return rtno;
    int l_LCA=LCA(lno.u,lno.v);
    int r_LCA=LCA(rno.u,rno.v);
     
    /*if(LCA(r_LCA,lno.u)==r_LCA&&LCA(l_LCA,rno.u)==l_LCA) //l_ca->lno.u   r_ca->rno.u
 
    {
        rtno.u=LCA(lno.u,rno.u);
    }
    else if(LCA(r_LCA,lno.u)==r_LCA&LCA(l_LCA,rno.v)==l_LCA)   //l_ca->lno.u   r_ca->rno.v
    {
        rtno.u=LCA(lno.u,rno.v);
    }
 
    if(LCA(r_LCA,lno.v)==r_LCA&&LCA(l_LCA,rno.v)==l_LCA)  //l_ca->lno.v   r_ca->rno.v
    {
        rtno.v=LCA(lno.v,rno.v);
    }
    else if(LCA(r_LCA,lno.v)==r_LCA&LCA(l_LCA,rno.u)==l_LCA)  //l_ca->lno.v   r_ca->rno.u
    {
        rtno.v=LCA(lno.v,rno.u);
    }*/
    int z[4];
    z[0]=LCA(lno.u,rno.u),z[1]=LCA(lno.u,rno.v),z[2]=LCA(lno.v,rno.u),z[3]=LCA(lno.v,rno.v);
    sort(z,z+4,cmp);
    rtno.u=z[3];
    rtno.v=z[2];
    if(rtno.u==-1||rtno.v==-1) return rtno;
 
    int maxds=-1;
    int tmp=cal_dis(lno.du,rtno.u);
    if(tmp>maxds) rtno.du=lno.du,maxds=tmp;
    tmp=cal_dis(lno.dv,rtno.u);
    if(tmp>maxds) rtno.du=lno.dv,maxds=tmp;
    tmp=cal_dis(rno.du,rtno.u);
    if(tmp>maxds) rtno.du=rno.du,maxds=tmp;
    tmp=cal_dis(rno.dv,rtno.u);
    if(tmp>maxds) rtno.du=rno.dv,maxds=tmp;
 
 
    maxds=-1;
    tmp=cal_dis(lno.du,rtno.v);
    if(tmp>maxds) rtno.dv=lno.du,maxds=tmp;
    tmp=cal_dis(lno.dv,rtno.v);
    if(tmp>maxds) rtno.dv=lno.dv,maxds=tmp;
    tmp=cal_dis(rno.du,rtno.v);
    if(tmp>maxds) rtno.dv=rno.du,maxds=tmp;
    tmp=cal_dis(rno.dv,rtno.v);
    if(tmp>maxds) rtno.dv=rno.dv,maxds=tmp;
     
    return rtno;
}
 
 
void build(int l,int r,int root)  //l,r表示他们在stu中的下标,root表示他们在线段树中的坐标
{
    if(l>r)return;
    if(l==r)
    {
        //Btree[root].y=b[l];
        Btree[root]=stu[l];
        return ;
    }
  
    int mid=(l+r)>>1;
     
    build(l,mid,root<<1);
    build(mid+1,r,(root<<1)|1);
  
     
    Btree[root]=push_up(Btree[lch],Btree[rch]); 
}
 
 
 

 
 
node query(int root,int s1,int e1,int s2,int e2)
{
     
    if(s1>=s2&&e1<=e2)
    {
        return Btree[root];
    }
  
    //pushDown(root);
    int mid=(s1+e1)>>1;
    node lno,rno;
    lno.v=lno.u=-1;
    rno.u=rno.v=-1;
    if(s2<=mid)  lno=query(root<<1,s1,mid,s2,e2);
    if(mid+1<=e2) rno=query((root<<1)|1,mid+1,e1,s2,e2);
    if(s2<=mid&&mid+1>e2) return lno;
    else if(mid+1<=e2&&s2>mid) return rno;
    else return push_up(lno,rno);
}
 
void updateone(int root,int s1,int e1,node x,int ind)  
{
    if(s1>e1)return;
    if(s1==e1&&s1==ind)
    {
        Btree[root]=x;
        return;
    }
    int mid=(s1+e1)>>1;
    if(mid>=ind)
        updateone(root<<1,s1,mid,x,ind);
    else
        updateone((root<<1)|1,mid+1,e1,x,ind);
    Btree[root]=push_up(Btree[lch],Btree[rch]);
}
 
 
 

 
/********LCA(ST)**********/
int pos[N<<1],Log[N],ST[N<<1][25]; //pos[i]:i第一次出现的位置,ST[i][j]在欧拉序[i,i+(1<<j))中dep最小的点
int tot;
int fa[N][25]; //fa[i][j]记录第i个节点的第(1<<j)个父亲,(非必要)

int Min(int x,int y) { 
	return dep[x] < dep[y] ? x : y;
}


void dfs(int u)
{
	ST[++tot][0] = u; pos[u] = tot;
	for (int i = 0; i<ee[u].size(); i ++) {
		int v = ee[u][i];
		if (v == fa[u][0]) continue;
		fa[v][0] = u, dep[v] = dep[u] + 1;
		
		dfs(v);
		ST[++tot][0] = u;//!
	}

}

void init(int n)
{
	tot=0;
	dfs(1);
	Log[0] = -1;
	for (int i = 1; i <= tot; ++i) Log[i] = Log[i >> 1] + 1;
	for (int j = 1; j <= Log[n]; ++j) 
		for (int i = 1; i <= n; ++i) fa[i][j] = fa[fa[i][j - 1]][j - 1];
	for (int j = 1; j <= Log[tot]; ++j) 
		for (int i = 1; i <= tot; ++i) ST[i][j] = Min(ST[i][j - 1], ST[i + (1 << (j - 1))][j - 1]);

}

int LCA(int u,int v) {
	if (u == v) return u;
	u = pos[u], v = pos[v];
	if (u > v) swap(u, v); 
	//u ++;   //?
	int k = Log[v - u + 1];
	return Min(ST[u][k], ST[v - (1 << k) + 1][k]);
}

 
 


/********LCA(ST)**********/



int find_mid(node z)
{
    int x=z.du;
    int y=z.dv;
    int rt_mid=LCA(x,y);
    int dis=dep[x]-dep[rt_mid]+dep[y]-dep[rt_mid];
    int half=dis/2;
    int delta;
    int p1,p2;
    if(cal_dis(x,rt_mid)>=half) //找距离x,half长度的点     
    {//cal_dis(x,rt_mid)
        delta=half;
        p1=x;
    }
    else
    {
        delta=dis-half;
        p1=y;
    }
    for(int i=0;i<=max0;i++)    
        if((1<<i)&delta)
        {
            //ans+=data[u][x];
            p1=fa[p1][i];
        }
 
    if(cal_dis(y,rt_mid)>=half)    //找距离y,half长度的点
    {//cal_dis(y,rt_mid)
        delta=half;
        p2=y;
    }
    else
    {
        delta=dis-half;
        p2=x;
    }
     
    for(int i=0;i<=max0;i++)   
        if((1<<i)&delta)
        {
            //ans+=data[u][x];
            p2=fa[p2][i];
        }
    int ans=min(p1,p2);
    int rt=LCA(z.u,z.v);
    if(LCA(ans,rt)==rt&&(LCA(ans,z.u)==ans||(LCA(ans,z.v)==ans)))
    {
        return ans;
    }
    else
    {
        if(cal_dis(ans,z.u)>cal_dis(ans,z.v))
        {
            return z.v;
        }
        else
        {
            return z.u;
        }
    }
 
 
}


int main()
{
    int n,m;

    scanf("%d%d",&n,&m);
    for(int i=2;i<=n;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        ee[u].push_back(v);
        ee[v].push_back(u);
    }
	init(n);
    for(int i=1;i<=m;i++)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        stu[i].u=u;
        stu[i].v=v;
        stu[i].du=v;
        stu[i].dv=u;
    }
    build(1,m,1);
 
    int q;
    scanf("%d",&q);
    for(int i=0;i<q;i++)
    {
        int id,x,y;
        scanf("%d%d%d",&id,&x,&y);
        if(id==1)
        {
            node u=query(1,1,m,x,y);
            if(u.u==-1) printf("-1\n");
            else printf("%d\n",find_mid(u));
        }
        else
        {
            int v;
            scanf("%d",&v);
            node now;
            now.u=y;now.v=v;
            now.du=v;now.dv=y;
            updateone(1,1,m,now,x);
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值