越野赛车问题——线段树分治+并查集

题目

【题目描述】
小 $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 imes 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
",ans[i]);
88     return 0;
89 }
View Code
原文地址:https://www.cnblogs.com/chmwt/p/10590489.html