题意:
给你一颗树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
这道题核心的部分其实就是用线段树来维护一个数据结构(这里是维护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);
}
}
}