zoukankan      html  css  js  c++  java
  • HDU6393(LCA + RMQ + 树状数组) n边图,两点最短距离 , 修改边权

    这道题的进阶版本

    进阶版本

    题意:

    一个n个点,n条边的图,2中操作,1是将某条边的权值更改,2是询问两点的最短距离。

    题解: 

    由于n个点,n条边,所以是树加一个环,将环上的边随意取出一条,就是1颗树,以取出的边的一个端点为根,建立有根树。虚线就是取出的边。红色为环上的边。

     

    对于更改边的权值的操作,用dfs序+区间修改点查询的树状树组维护。

    对于询问最短路的操作,用LCA分类解决。假设询问的两点是x、y,LCA是 z。

    若z不是环上的点,那么最短路就是:x到根的距离+y到根的距离-z到根的距离*2;
    若z是环上的点,最短路可能是经过图中红线的路径,也可能是经过图中虚线的路径,取最短的即可

    #include<bits/stdc++.h>
    #define lson (i<<1)
    #define rson (i<<1|1)
     
    using namespace std;
    typedef long long ll;
    const int N =1e5+5;
     
    struct node
    {
        int u,v,next;
        int id,f;
        ll w;
    }edge[N*2];
     
    struct node1
    {
        int l,r;
        ll w;
        ll lz;
    }tr[N<<2];
     
    int tot,head[N];
    int n,q;
    int anc[N<<1][20];
     
    int dfn;
    int dfns[N*2];
    int dep[N*2];
    int pos[N];
    int inde[N];
    int L[N];
    int R[N];
    int clo;
     
    int to[N];
    int vis[N];
    ll ww[N];
     
    int uu,vv;
    ll cost;
    int huan;
    int idd;
     
    void init()
    {
        tot=0;
        memset(head,-1,sizeof(head));
        memset(vis,0,sizeof(vis));
        memset(inde,-1,sizeof(inde));
        memset(pos,-1,sizeof(pos));
        clo=0; huan=0; idd=0; dfn=0;  /// dfn竟然没清零 MMP
    }
     
    void add(int u,int v,ll w,int id)
    {
        edge[++tot].u=u; edge[tot].v=v; edge[tot].id=id; edge[tot].w=w; edge[tot].f=0;
        edge[tot].next=head[u]; head[u]=tot;
    }
     
    void dfs1(int u,int fa)
    {
        if(vis[u]){
            uu=fa; vv=u; huan=1; return ;
        }
        vis[u]=1;
        for(int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].v;
            if(v==fa) continue;
            dfs1(v,u);
        }
    }
     
    void dfs2(int u,int deep) /// dfs序
    {
        //cout<<" u "<<u<<" deep "<<deep<<endl;
        dfns[dfn]=u; dep[dfn]=deep; pos[u]=dfn++;
        L[u]=++clo;
        inde[u]=L[u]; /// 记录u在线段树中的位置
        for(int i=head[u];i!=-1;i=edge[i].next){
            if(edge[i].f) continue;  /// 如果是标记的边跳过
            int v=edge[i].v;
            if(pos[v]==-1){
                to[edge[i].id]=v; /// 表示这条边指向哪个点?
                dfs2(v,deep+1);
                dfns[dfn]=u; dep[dfn++]=deep;
            }
        }
        R[u]=clo;
    }
     
    void init_RMQ(int n)  /// dfn
    {
        for(int i=1;i<=n;++i) anc[i][0]=i;
        for(int j=1;(1<<j)<=n;++j)
            for(int i=1;i+(1<<j)-1<=n;++i){
                if(dep[anc[i][j-1]]<dep[anc[i+(1<<(j-1))][j-1]]) anc[i][j]=anc[i][j-1];
                else anc[i][j]=anc[i+(1<<(j-1))][j-1];
            }
    }
     
    inline int RMQ(int L,int R)
    {
        int k=0;
        while(1<<(k+1)<=R-L+1) ++k;
        if(dep[anc[L][k]]<dep[anc[R-(1<<k)+1][k]]) return anc[L][k];
        return anc[R-(1<<k)+1][k];
    }
     
    inline int LCA(int u,int v)
    {
        if(pos[u]>pos[v]) return dfns[RMQ(pos[v],pos[u])];
        return dfns[RMQ(pos[u],pos[v])];
    }
     
    void push_up(int i)
    {
        tr[i].w=tr[lson].w+tr[rson].w;
    }
     
    void push_down(int i)
    {
        if(tr[i].lz){ /// 查询只有点查询,所以不必更新区间点的sum
            ll &lz=tr[i].lz;
            tr[lson].lz+=lz; tr[rson].lz+=lz;
            tr[lson].w+=lz;  tr[rson].w+=lz;
            lz=0;
        }
    }
     
    void build(int i,int l,int r)
    {
        tr[i].l=l; tr[i].r=r; tr[i].w=0; tr[i].lz=0;
        if(l==r) return ;
        int mid=(l+r)>>1;
        build(lson,l,mid);
        build(rson,mid+1,r);
    }
     
    void update(int i,int l,int r,ll w)
    {
        if(tr[i].l==l&&tr[i].r==r){
            tr[i].lz+=w; tr[i].w+=w;
            return ;
        }
        push_down(i);
        int mid=(tr[i].l+tr[i].r)>>1;
        if(r<=mid) update(lson,l,r,w);
        else if(l>mid ) update(rson,l,r,w);
        else{
            update(lson,l,mid,w);
            update(rson,mid+1,r,w);
        }
        push_up(i);
    }
     
    ll query(int i,int aim)
    {
        if(tr[i].l==tr[i].r&&tr[i].l==aim){
            return tr[i].w;
        }
        push_down(i);
        int mid=(tr[i].l+tr[i].r)>>1;
        if(aim<=mid) return query(lson,aim);
        else return query(rson,aim);
    }
     
    ll getans(int u,int v)
    {
        int lca=LCA(u,v);
        ll sum1,sum2,sum3;
        sum1=query(1,L[u]); sum2=query(1,L[v]);
        sum3=query(1,L[lca]);
        return sum1+sum2-sum3*2;
    }
     
    int main()
    {
        int T;
        int u,v,op;
        ll w;
        scanf("%d",&T);
        while(T--)
        {
            scanf("%d %d",&n,&q);
            init();
            for(int i=1;i<=n;i++){
                scanf("%d %d %lld",&u,&v,&w);
                add(u,v,w,i);
                add(v,u,w,i);
                ww[i]=w;
            }
     
            dfs1(1,-1);/// 第一遍dfs 先找到环中的任意一条边
            for(int i=1;i<=tot;i++){  /// 给边打上标记
                if((edge[i].u==uu&&edge[i].v==vv)||(edge[i].u==vv&&edge[i].v==uu)){
                    edge[i].f=1;
                    idd=edge[i].id;
                    cost=edge[i].w;
                }
            }
            dfs2(1,0);
            /*cout<<"dfn "<<dfn<<endl;
            cout<<uu<<" *** "<<vv<<endl;
            for(int i=1;i<=n;i++){
                cout<<"l "<<L[i]<<" r "<<R[i]<<endl;
            }
            */
            init_RMQ(dfn);
     
     
            build(1,1,n);  /// 以dfs的遍历出的 L,R 建树  那么接下来就是一个区间更新,单点查询的问题了
            for(int i=1;i<=n;i++){
                if(i==idd) continue;
                u=to[i];
                update(1,L[u],R[u],ww[i]);
            }
            /*
            for(int i=1;i<=n;i++){
                ll tmp=query(1,L[i]);
                cout<<" dis "<<tmp<<endl;
            }
            */
     
            while(q--)
            {
                scanf("%d",&op);
                if(op==0){
                    scanf("%d %lld",&u,&w);
                    if(u==idd){ /// 如果是标记的环中的边,那么就没必要更新线段树
                        ww[u]=w;
                    }
                    else{
                        int tmp=to[u];
                        //cout<<"tmp "<<tmp<<" L "<<L[tmp]<<" R "<<R[tmp]<<" w "<<ww[u]<<endl;
                        update(1,L[tmp],R[tmp],-ww[u]);
                        ww[u]=w;
                        update(1,L[tmp],R[tmp],ww[u]);
                    }
                }
                else{
                    scanf("%d %d",&u,&v);
                    ll ans=getans(u,v);
                    ans=min(ans,getans(uu,u)+getans(vv,v)+cost); /// 经过标记的路的两个不同的方向。
                    ans=min(ans,getans(uu,v)+getans(vv,u)+cost);
                    printf("%lld
    ",ans);
                }
            }
     
        }
        return 0;
    }
    View Code
  • 相关阅读:
    字符菱形
    字符三角形
    10:超级玛丽游戏
    09:字符菱形
    08:字符三角形
    07:输出浮点数
    06:空格分隔输出
    05:输出保留12位小数的浮点数
    04:输出保留3位小数的浮点数
    02:输出第二个整数
  • 原文地址:https://www.cnblogs.com/shuaihui520/p/10439897.html
Copyright © 2011-2022 走看看