#4259. 越野赛车问题
题意
内存限制:256 MiB
时间限制:1000 ms
小 $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$ 次训练中计划使用的车速,请帮助她对于每次训练,找到一条合法的路径(车速在所有车道的速度承受区间的交集内),使得路径上经过的车道数最大。
$1 \leq n,m,l_i,r_i,v_i \leq 70000$
题解
建一棵以速度为下标的线段树,把树上的每条边放到线段树中,然后对于询问进行线段树分治,每次走到一个区间就将里面的每条边的两个端点连成一个并查集,这个并查集要维护的信息是里面的点中的最长链,由于之后要撤销,所以不能路径压缩,改用启发式合并
对于维护最长链,只需要将原来两个并查集的四个点拿出来,分别跑出路径大小,判断即可,这里需要用 $rmq$ 维护两点间的 $lca$
代码
#include <bits/stdc++.h> #define I inline using namespace std;const int N=70005; int n,m,t,f[N*2][18],c,d[N],fa[N],X[N],Y[N],L[N]; int R[N],ans[N],sz[N],e[N],hd[N],V[N*2],nx[N*2]; vector<int>g[N*4];struct O{int v,id;}p[N];struct far{int x,y;}h[N]; struct E{int x,f,s;far k;};vector<E>a[N*4]; I void add(int u,int v){V[++t]=v;nx[t]=hd[u];hd[u]=t;} I void dfs(int x,int F){ f[++c][0]=x;d[x]=d[F]+1;e[x]=c; for (int i=hd[x];i;i=nx[i]) if (V[i]!=F) dfs(V[i],x),f[++c][0]=x; } I int lca(int l,int r){ if (l>r) swap(l,r);int i=log2(r-l+1); if (d[f[l][i]]<d[f[r-(1<<i)+1][i]]) return f[l][i];return f[r-(1<<i)+1][i]; } I bool cmp(O A,O B){return A.v<B.v;} I int get(int x){return x==fa[x]?x:get(fa[x]);} I int dis(int x,int y){return d[x]+d[y]-2*d[lca(e[x],e[y])];} #define Ls k<<1 #define Rs k<<1|1 #define mid ((l+r)>>1) I void update(int k,int l,int r,int ql,int qr,int v){ if (ql<=l && r<=qr){g[k].push_back(v);return;} if (mid>=ql) update(Ls,l,mid,ql,qr,v); if (mid<qr) update(Rs,mid+1,r,ql,qr,v); } I void query(int k,int l,int r,int ql,int qr,int ax){ for (int v,i=g[k].size()-1;~i;i--){ v=g[k][i];int k1=get(X[v]),k2=get(Y[v]); if (sz[k1]<sz[k2]) swap(k1,k2); a[k].push_back((E){k2,k1,sz[k1],h[k1]}); int T[2][2],now=0; T[0][0]=h[k1].x;T[0][1]=h[k1].y; T[1][0]=h[k2].x;T[1][1]=h[k2].y; for (int X1=0;X1<2;X1++) for (int Y1=0;Y1<2;Y1++) for (int X2=0;X2<2;X2++) for (int Y2=0;Y2<2;Y2++) if ((X1^X2) || (Y1^Y2)){ int nw=dis(T[X1][Y1],T[X2][Y2]); if (nw>now) now=nw,h[k1]=(far){T[X1][Y1],T[X2][Y2]}; } fa[k2]=k1;sz[k1]+=sz[k2];ax=max(ax,now); } if (l^r){ int T=ql;for (;T<=qr;T++) if (p[T].v>mid) break; if (T>ql) query(Ls,l,mid,ql,T-1,ax); if (T<=qr) query(Rs,mid+1,r,T,qr,ax); } else for (int i=ql;i<=qr;i++) ans[p[i].id]=ax; for (int i=a[k].size()-1;~i;i--) sz[a[k][i].f]=a[k][i].s,h[a[k][i].f]=a[k][i].k, fa[a[k][i].x]=a[k][i].x; } int main(){ scanf("%d%d",&n,&m); for (int i=1;i<n;i++) scanf("%d%d%d%d",&X[i],&Y[i],&L[i],&R[i]), add(X[i],Y[i]),add(Y[i],X[i]), update(1,1,n,L[i],R[i],i); dfs(1,0);for (int i=c;i;i--) for (int j=1;i+(1<<j)<=c+1;j++){ if (d[f[i][j-1]]<d[f[i+(1<<(j-1))][j-1]]) f[i][j]=f[i][j-1]; else f[i][j]=f[i+(1<<(j-1))][j-1]; } for (int i=1;i<=m;i++) scanf("%d",&p[i].v),p[i].id=i; for (int i=1;i<=n;i++) fa[i]=i,sz[i]=1,h[i]=(far){i,i}; sort(p+1,p+m+1,cmp);query(1,1,n,1,m,0); for (int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; }