Dash speed

题目大意

一颗树,每条边有边权[l,r]。
多次询问每次给定一个权值v,保留树上所有的边[l,r]使得l<=v且v<=r,然后求这片森林的最长简单路径长度。

经典CDQ

考虑分治算法
把每条边插入线段树区间中
然后一个权值的答案就是加入根到其对应叶子路径上经过的线段树节点上的所有边。
然后对线段树进行遍历,用并查集维护集合内的直径。因为要支持撤销,所以只能按秩合并不能路径压缩。
然后我跑的贼慢。

#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=70000+10,maxm=maxn*18*4+10,R=40*70000+10;
char buf[R+7],*ptr=buf-1;
int h[maxn],go[maxn<<1],next[maxn<<1],a[maxn*18][4];
int h2[maxn<<2],g2[maxm],n2[maxm];
int fa[maxn],rank[maxn],data[maxn][3];
int sta[maxn][6];//0~1 lianjie na liangge 2 rank[y] 3~4 data[y] 5 mx
bool ad[maxn];
int rmq[21][maxn<<1],ans[maxn],q[maxn],d[maxn],dfn[maxn<<1],fi[maxn],s[100];
int two[20];
bool pd[maxn];
int i,j,k,l,r,t,n,m,tot,top,pot,cnt,now,mx;
inline int read(){
    int x=0,c=*++ptr;
    while(c<48)c=*++ptr;
    while(c>47)x=x*10+c-48,c=*++ptr;
    return x;
}
inline void add(int x,int y){
    go[++tot]=y;
    next[tot]=h[x];
    h[x]=tot;
}
inline void dfs(int x,int y){
    d[x]=d[y]+1;
    dfn[++top]=x;
    fi[x]=top;
    int t=h[x];
    while (t){
        if (go[t]!=y){
            dfs(go[t],x);
            dfn[++top]=x;
        }
        t=next[t];
    }
}
inline void add2(int x,int y){
    g2[++cnt]=y;
    n2[cnt]=h2[x];
    h2[x]=cnt;
}
inline void newnode(int x){
    ++tot;
    a[tot][0]=a[x][0];a[tot][1]=a[x][1];
    a[tot][2]=a[x][2];a[tot][3]=a[x][3];
}
inline int getfa(int x){
    while (fa[x]) x=fa[x];
    return x;
}
inline int lca(int x,int y){
    int l=fi[x],r=fi[y];
    if (l>r) swap(l,r);
    int z=floor(log(r-l+1)/log(2));
    int j=rmq[z][l],k=rmq[z][r-two[z]+1];
    if (d[dfn[j]]<d[dfn[k]]) return dfn[j];else return dfn[k];
}
inline int getdis(int x,int y){
    int z=lca(x,y);
    return d[x]+d[y]-(d[z]<<1);
}
inline void merge(int x,int y){
    int a=getfa(x),b=getfa(y);
    if (rank[a]>rank[b]) swap(a,b);
    //if (a==b) return;
    sta[++pot][0]=a;
    sta[pot][1]=b;
    sta[pot][2]=data[b][2];
    sta[pot][3]=data[b][0];
    sta[pot][4]=data[b][1];
    sta[pot][5]=mx;
    fa[a]=b;
    if (rank[a]==rank[b]) rank[b]++,ad[pot]=1;else ad[pot]=0;
    int i,j,ans1=data[b][0],ans2=data[b][1],l=data[b][2],t;
    t=data[a][2];
    if (t>l){
        ans1=data[a][0];
        ans2=data[a][1];
        l=t;
    }
    t=getdis(data[a][0],data[b][0]);
    if (t>l){
        ans1=data[a][0];
        ans2=data[b][0];
        l=t;
    }
    t=getdis(data[a][0],data[b][1]);
    if (t>l){
        ans1=data[a][0];
        ans2=data[b][1];
        l=t;
    }
    t=getdis(data[a][1],data[b][0]);
    if (t>l){
        ans1=data[a][1];
        ans2=data[b][0];
        l=t;
    }
    t=getdis(data[a][1],data[b][1]);
    if (t>l){
        ans1=data[a][1];
        ans2=data[b][1];
        l=t;
    }
    if (l>mx) mx=l;
    data[b][0]=ans1;data[b][1]=ans2;data[b][2]=l;
}
inline void re(int x){
    int j=sta[x][0],k=sta[x][1];
    fa[j]=0;
    /*if (sta[x][2]!=rank[k])*/ if (ad[x]) rank[k]--;
    data[k][2]=sta[x][2];
    /*if (data[k][0]!=sta[x][3])*/ data[k][0]=sta[x][3];
    /*if (data[k][1]!=sta[x][4])*/ data[k][1]=sta[x][4];
    /*if (mx!=sta[x][5])*/ mx=sta[x][5];
}
inline void solve(int p,int l,int r){
    int t=h2[p],i,j,k,now,tmp=pot,mid=(l+r)>>1;
    while (t){
        now=g2[t];
        if (a[now][2]==l&&a[now][3]==r) merge(a[now][0],a[now][1]);
        else if (a[now][3]<=mid) add2((p<<1),now);
        else if (a[now][2]>mid) add2((p<<1)|1,now);
        else{
            newnode(now);
            a[tot][2]=mid+1;
            add2((p<<1)|1,tot);
            a[now][3]=mid;
            add2((p<<1),now);
        }
        t=n2[t];
    }
    if (l==r) ans[l]=mx;
    else{
        solve((p<<1),l,mid);
        solve((p<<1)|1,mid+1,r);
    }
    while (pot>tmp){
        re(pot);
        pot--;
    }
}
inline void write(int x){
    if (!x){
        putchar('0');
        putchar('\n');
        return;
    }
    top=0;
    while (x){
        s[++top]=x%10;
        x/=10;
    }
    while (top){
        putchar('0'+s[top]);
        top--;
    }
    putchar('\n');
}
int main(){
    freopen("speed.in","r",stdin);freopen("speed.out","w",stdout);
    fread(buf,1,R,stdin);
    n=read();m=read();
    fo(i,1,n-1){
        j=read();k=read();l=read();r=read();
        add(j,k);add(k,j);
        a[i][0]=j;
        a[i][1]=k;
        a[i][2]=l;
        a[i][3]=r;
    }
    fo(i,1,m){
        q[i]=read();
    }
    dfs(1,0);
    fo(i,1,top) rmq[0][i]=i;
    t=floor(log(top)/log(2));
    two[0]=1;
    fo(i,1,t) two[i]=(two[i-1]<<1);
    fo(j,1,t)
        fo(i,1,top-two[j]+1){
            k=rmq[j-1][i];l=rmq[j-1][i+two[j-1]];
            if (d[dfn[k]]<d[dfn[l]]) rmq[j][i]=k;else rmq[j][i]=l;
        }
    tot=n-1;
    fo(i,1,n-1) add2(1,i);
    fo(i,1,n) data[i][0]=data[i][1]=i;
    solve(1,1,n);
    fo(i,1,m) write(ans[q[i]]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值