越野赛车问题——线段树分治+并查集
题目
【题目描述】
小 $H$ 是一位优秀的越野赛车女选手。现在她准备在 $A$ 山上进行赛车训练。
$A$ 山上一共有 $n$ 个广场,编号依次为 $1$ 到 $n$ ,这些广场之间通过 $n-1$条双
向车道直接或间接地连接在一起。对于每条车道$i$ ,可以用四个正整数 $u_i,v_i,l_i,r_i$描
述,表示车道连接广场 $u_i$ 和 $v_i$ ,其速度承受区间为$[l_i,r_i]$,即汽车必须以不小于
$l_i$ 且不大于 $r_i$ 的速度经过车道$i$ 。
小 $H$ 计划进行 $m$ 次训练,每次她需要选择 $A$ 山上的一条简单路径,然后在
这条路径上行驶。但小 $H$ 不喜欢改变速度,所以每次训练时的车速都是固定的。
现在小 $H$ 告诉你她在$m$ 次训练中计划使用的车速,请帮助她对于每次训练,找
到一条合法的路径(车速在所有车道的速度承受区间的交集内),使得路径上经
过的车道数最大。
【输入格式】
输入文件的第一行包含两个正整数 $n,m$ ,表示广场数和训练次数。
接下来 $n-1$ 行,每行四个正整数 $u_i,v_i,l_i,r_i(\leq n)$,描述所有车道。
最后 $m$ 行,每行一个正整数 $v(\leq n)$ ,表示小 $H$ 每次训练时使用的车速。
【输出格式】
输出 $m$ 行,将每次训练时的行驶路径经过的最大车道数输出到文件中。
【样例输入】
5 3
3 2 2 4
1 5 2 5
4 5 2 2
1 2 3 5
1
2
3
【样例输出】
0
2
3
【数据范围与提示】
保证 $n,m \leq 7 \times 10^4 $
题解
考虑分治
以赛道的限制为下标建线段树,将每一条边按 $ [l_i,r_i] $ 存入线段树
查询的时候按线段树分治,构建出 $ [l,r] $ 的联通块,然后求联通块内的最长路径
对于每一个联通块,记录下最长路的起点和终点 $ u,v $,合并两个联通块时,新的联通块的最长路的 $ u,v $ 一定是两个联通块的 $ u,v $,然后分讨一下即可
求 $ u,v $ 的路径长,可以树上倍增lca(不反对树剖),因为保证联通块在树上
但是由于联通块要支持撤回,并查集不能压缩路径,并且要记录下每一次的修改,然后在退出时撤回(不会写可持久化并查集)
代码
1 #include<bits/stdc++.h> 2 #define LL long long 3 #define pb push_back 4 #define _(d) while(d(isdigit(ch=getchar()))) 5 using namespace std; 6 int R(){ 7 int x;bool f=1;char ch;_(!)if(ch=='-')f=0;x=ch^48; 8 _()x=(x<<3)+(x<<1)+(ch^48);return f?x:-x;} 9 const int N=1e5+5; 10 int n,m,head[N],cnt,f[N][22],lg,dep[N],fa[N],sz[N],ans[N],h[5]; 11 vector<int>tr[N<<2]; 12 struct edge{int to,nex;}e[N<<1]; 13 struct load{int u,v;}s[N],p[N]; 14 struct node{int v,id;}q[N]; 15 bool cmp(node a,node b){return a.v<b.v;} 16 struct pig{int u,v,s;load x;}; 17 vector<pig>b[N<<2]; 18 void add(int s,int t){e[++cnt]=(edge){t,head[s]},head[s]=cnt;} 19 #define Ls rt<<1 20 #define Rs rt<<1|1 21 void update(int rt,int l,int r,int ql,int qr,int x){ 22 if(ql<=l&&qr>=r){tr[rt].pb(x);return;} 23 int mid=(l+r)>>1; 24 if(ql<=mid)update(Ls,l,mid,ql,qr,x); 25 if(qr>mid)update(Rs,mid+1,r,ql,qr,x); 26 return; 27 } 28 void dfs(int u,int far){ 29 f[u][0]=far,dep[u]=dep[far]+1; 30 for(int i=1;i<=lg;i++)f[u][i]=f[f[u][i-1]][i-1]; 31 for(int v,k=head[u];k;k=e[k].nex) 32 if((v=e[k].to)!=far) 33 dfs(v,u); 34 return; 35 } 36 int lca(int x,int y){ 37 if(dep[x]>dep[y])swap(x,y); 38 int i=lg; 39 while(dep[x]<dep[y]){ 40 while(i>0&&dep[x]>dep[f[y][i]])i--; 41 y=f[y][i];} 42 i=lg; 43 while(x!=y){ 44 while(i>0&&f[x][i]==f[y][i])i--; 45 x=f[x][i],y=f[y][i];} 46 return x; 47 } 48 int find(int x){return fa[x]==x?x:find(fa[x]);} 49 void slove(int rt,int l,int r,int ql,int qr,int len){ 50 for(int j=tr[rt].size()-1;~j;j--){ 51 int k=tr[rt][j],u=s[k].u,v=s[k].v,fx=find(u),fy=find(v),w=0; 52 h[1]=p[fx].u,h[2]=p[fx].v,h[3]=p[fy].u,h[4]=p[fy].v; 53 if(sz[fx]>sz[fy])swap(fx,fy); 54 b[rt].pb((pig){fx,fy,sz[fy],p[fy]}); 55 fa[fx]=fy,sz[fy]+=sz[fx]; 56 for(int x=1;x<4;x++) 57 for(int y=x+1;y<=4;y++){ 58 int u=h[x],v=h[y],far=lca(u,v),dis=dep[u]+dep[v]-dep[far]*2; 59 if(dis>w)w=dis,p[fy]=(load){u,v}; 60 } 61 len=max(len,w); 62 } 63 if(l^r){ 64 int k=ql,mid=(l+r)>>1; 65 while(q[k].v<=mid&&k<=qr)k++; 66 if(k>ql)slove(Ls,l,mid,ql,k-1,len); 67 if(k<=qr)slove(Rs,mid+1,r,k,qr,len); 68 } 69 else for(int i=ql;i<=qr;i++)ans[q[i].id]=len; 70 71 for(int j=b[rt].size()-1;~j;j--) 72 sz[b[rt][j].v]=b[rt][j].s,fa[b[rt][j].u]=b[rt][j].u,p[b[rt][j].v]=b[rt][j].x; 73 return; 74 } 75 76 int main(){ 77 n=R(),m=R(),lg=log2(n)+1; 78 for(int i=1;i<n;i++){ 79 int u=R(),v=R(),li=R(),ri=R(); 80 add(u,v),add(v,u),update(1,1,n,li,ri,i); 81 s[i]=(load){u,v}; 82 } 83 for(int i=1;i<=m;i++)q[i]=(node){R(),i}; 84 sort(q+1,q+1+m,cmp); 85 for(int i=1;i<=n;i++)fa[i]=i,sz[i]=1,p[i]=(load){i,i}; 86 dfs(1,0);slove(1,1,n,1,m,0); 87 for(int i=1;i<=m;i++)printf("%d\n",ans[i]); 88 return 0; 89 }