#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;
}

 

posted @ 2019-03-21 12:33  xjqxjq  阅读(224)  评论(0编辑  收藏  举报