【GDOI2017模拟11.5】Dash Speed

Description

给出n个点的一棵树,每条边有一个限制区间[l,r]。
给出m次询问,每次询问给出一个数x,求一条最长的所有边都满足l<=x<=r的路径。
n,m<=7*10^4

Solution

一开始看错数据范围,以为是7*10^5,虚的我。。。
然而没想到每次询问就是相当于保留所有l<=x<=r的边,求森林的直径
考虑分治(据说这也是cdq分治)
首先把每一条边放到所有它完全包含的分治区间中。
然后当我们做到当前区间时,把这个区间中的所有边加入森林中去。
回溯时候再把这些边删掉。
不过因为要兹瓷删边,所以我们的并查集不能路径压缩,只能按秩合并。。。
跑的慢死了。。。

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define rep(i,a) for(int i=last[a];i;i=next[i])
using namespace std;
const int N=7*1e4+5;
struct note{int a,pa,pb,x,bz;}pr[N*20];
int n,m,cnt,tot,ans,x,y,l,r,mx,mi[18],ln[N*2];
int d[N],f[N],fir[N],dfn[N*2],g[N*2][18],p[N][2],q[N*20],rank[N],an[N];
int t[N*2],next[N*2],last[N],la[N*4],ne[N*20],A[N*20],B[N*20];
void add(int x,int y) {
    t[++cnt]=y;next[cnt]=last[x];last[x]=cnt;
}
int max(int x,int y) {
    if (x>y) return x;else return y;
}
void insert(int v,int l,int r,int x,int y,int a,int b) {
    if (x<=l&&r<=y) {
        A[++tot]=a;B[tot]=b;mx=max(mx,v);
        ne[tot]=la[v];la[v]=tot;
        return;
    }
    int m=(l+r)/2;
    if (x<=m) insert(v*2,l,m,x,y,a,b);
    if (y>m) insert(v*2+1,m+1,r,x,y,a,b);
}
void dfs(int x,int y) {
    d[x]=d[y]+1;dfn[++tot]=x;fir[x]=tot;
    rep(i,x) if (t[i]!=y) dfs(t[i],x),dfn[++tot]=x;
}
int get(int x) {
    return f[x]?get(f[x]):x;
}
int lca(int x,int y) {
    x=fir[x];y=fir[y];
    if (x>y) swap(x,y);
    int z=ln[y-x+1];
    if (d[dfn[g[x][z]]]<d[dfn[g[y-mi[z]+1][z]]]) return dfn[g[x][z]];
    else return dfn[g[y-mi[z]+1][z]];
}
int len(int x,int y) {
    int z=lca(x,y);
    return d[x]+d[y]-2*d[z];
}
void merge(int x,int &ans) {
    int ax=get(A[x]),ay=get(B[x]);tot++;
    int now[2],cnt;now[0]=p[ax][0];now[1]=p[ax][1];cnt=len(now[0],now[1]);
    int dist=len(p[ay][0],p[ay][1]);
    if (dist>cnt) cnt=dist,now[0]=p[ay][0],now[1]=p[ay][1];
    fo(j,0,1) fo(k,0,1) {
        dist=len(p[ax][j],p[ay][k]);
        if (dist>cnt) cnt=dist,now[0]=p[ax][j],now[1]=p[ay][k];
    }
    if (rank[ay]<rank[ax]) {
        pr[x].x=ay;pr[x].a=ax;
        pr[x].pa=p[ax][0];pr[x].pb=p[ax][1];
        p[ax][0]=now[0];p[ax][1]=now[1];
        f[ay]=ax;
    } else {
        pr[x].x=ax;pr[x].a=ay;
        pr[x].pa=p[ay][0];pr[x].pb=p[ay][1];
        p[ay][0]=now[0];p[ay][1]=now[1];
        f[ax]=ay;
        if (rank[ax]==rank[ay]) rank[ay]++,pr[x].bz=1;
    }
    ans=max(ans,len(now[0],now[1]));
}
void del(int x) {
    for(int i=la[x];i;i=ne[i]) q[++q[0]]=i;
    while (q[0]) {
        p[pr[q[q[0]]].a][0]=pr[q[q[0]]].pa;
        p[pr[q[q[0]]].a][1]=pr[q[q[0]]].pb;
        f[pr[q[q[0]]].x]=0;rank[pr[q[q[0]]].a]-=pr[q[q[0]]].bz;
        q[0]--;
    }
}
void work(int v,int l,int r,int ans) {
    for(int i=la[v];i;i=ne[i]) merge(i,ans);
    if (l==r) {
        an[l]=ans;
        del(v);
        return;
    }
    int m=(l+r)/2;
    work(v*2,l,m,ans);
    work(v*2+1,m+1,r,ans);
    del(v);
}
int main() {
    freopen("speed.in","r",stdin);
    freopen("speed.out","w",stdout);
    scanf("%d%d",&n,&m);
    fo(i,1,n-1) {
        scanf("%d%d%d%d",&x,&y,&l,&r);
        add(x,y);add(y,x);
        insert(1,1,n,l,r,x,y);
    }
    tot=0;dfs(1,0);mi[0]=1;
    fo(i,1,tot) g[i][0]=i,ln[i]=log(i)/log(2);fo(i,1,17) mi[i]=mi[i-1]*2;
    fo(j,1,17) fo(i,1,tot-mi[j]+1) 
        if (d[dfn[g[i][j-1]]]<d[dfn[g[i+mi[j-1]][j-1]]]) g[i][j]=g[i][j-1];
        else g[i][j]=g[i+mi[j-1]][j-1];
    fo(i,1,n) p[i][0]=p[i][1]=i;
    work(1,1,n,0);
    fo(i,1,m) scanf("%d",&x),printf("%d\n",an[x]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值