• 题解 P4949 【最短距离】


    吼题啊

    刚开始看上去又以为是LCT啥子的。

    后来发现,TM是个图。

    然后果断准备放弃,突然发现只有N个点N条边。

    woc,这不就一个基环树上树链剖分吗。。。

    关于基环树问题,相信大家都一定很有经验了吧,用个并查集找出多的边,然后把图分成一棵树和一条边,然后树上就树上做,多出来的边就可以另外处理。

    关于边权转点权,直接在查询的最后减去LCA的点权就OK了,其他当成点权处理。

    关于线段树,这个大家都会改吧。。。

    关于修改操作,直接找出相应的点权,然后修改就行了,找点权记得先边的编号乘2

    总之还是很简单的

    下面可以看看代码注释:

    #include<bits/stdc++.h>
    #define maxn 4000001
    #define L(x) (x<<1)
    #define R(x) ((x<<1)|1)
    using namespace std;
    int tree[maxn],tag[maxn];
    int rev[maxn],dep[maxn],size[maxn],seg[maxn],top[maxn],son[maxn],father[maxn];
    int n,m,root,x,y,z,a[maxn],visx[maxn],visy[maxn],tot,mode;
    int cnt,from[maxn],to[maxn],Next[maxn],head[maxn];
    int Gx,Gy,Gz,Gd;
    int fa[maxn],X[maxn],Y[maxn],Z[maxn];
    int find(int x){
        if(fa[x]==x)return x;
        else return fa[x]=find(fa[x]);
    }
    bool merge(int x,int y){
        int i=find(x),j=find(y);
        if(i==j)return false;
        fa[i]=j;return true;
    }
    void add(int x,int y){
        cnt++;
        from[cnt]=x;to[cnt]=y;
        Next[cnt]=head[x];head[x]=cnt;
    }
    void update(int node,int begin,int end,int x,int y,int val){
        if(begin>y||end<x)return;
        if(begin>=x&&end<=y){
            tree[node]=val;
            return;
        }else{
            int mid=(begin+end)>>1;
            if(x<=mid)update(L(node),begin,mid,x,y,val);
            if(y>mid) update(R(node),mid+1,end,x,y,val);
            tree[node]=tree[L(node)]+tree[R(node)];
        }
    }
    int query(int node,int begin,int end,int x,int y){
        if(begin>=x&&end<=y){
            return tree[node];
        }else{
            int mid=(begin+end)>>1,sum=0;
            if(x<=mid)sum+=query(L(node),begin,mid,x,y);
            if(y>mid) sum+=query(R(node),mid+1,end,x,y);
            return sum;
        }
    }
    int dfs1(int x){
        size[x]=1;
        dep[x]=dep[father[x]]+1;
        for(int i=head[x];i!=-1;i=Next[i]){
            int v=to[i],big=0;
            if(father[x]==v)continue;
            father[v]=x;
            big=dfs1(v);
            size[x]+=big;
            if(big>size[son[x]])son[x]=v;
        }
        return size[x]; 
    }
    void dfs2(int x){
        if(son[x]){
            seg[son[x]]=++seg[0];
            top[son[x]]=top[x];
            rev[seg[0]]=son[x];
            dfs2(son[x]);
        }
        for(int i=head[x];i!=-1;i=Next[i]){
            int v=to[i];
            if(!top[v]){
                seg[v]=++seg[0];
                top[v]=v;
                rev[seg[0]]=v;
                dfs2(v);
            }
        }
    }
    int linkquery(int x,int y){
        int fx=top[x],fy=top[y],ans=0;
        while(fx!=fy){
            if(dep[fx]<dep[fy])swap(x,y),swap(fx,fy);
            ans+=query(1,1,seg[0],seg[fx],seg[x]);
            x=father[fx];fx=top[x];
        }
        if(dep[x]>dep[y])swap(x,y);
        ans+=query(1,1,seg[0],seg[x],seg[y]);
        ans-=query(1,1,seg[0],seg[x],seg[x]);
        return ans;
    }
    void change(int x,int y){                       //修改边权
        x*=2;       //记得乘2
        x=dep[from[x]]>dep[to[x]]?from[x]:to[x];    //因为边权钦定的是深度大的点
        update(1,1,seg[0],seg[x],seg[x],y);         //直接改
    }
    int main(){
        memset(head,-1,sizeof(head));
        scanf("%d%d",&n,&m);root=1;
        for(int i=1;i<=n;i++)fa[i]=i;
        for(int i=1;i<=n;i++){
            scanf("%d%d%d",&x,&y,&z);
            if(merge(x,y)){add(x,y),add(y,x);}
            else Gx=x,Gy=y,Gz=z,Gd=i,cnt+=2;        //并查集判多的边
            X[i]=x;Y[i]=y;Z[i]=z;
        }
        dfs1(root);
        seg[root]=++seg[0];
        rev[seg[0]]=root;
        top[root]=root;
        dfs2(root);
        for(int i=1;i<=n;i++)if(Gd!=i)change(i,Z[i]);   //因为本人懒得写build,所以这样写
        for(int i=1;i<=m;i++){
            scanf("%d%d%d",&mode,&x,&y);
            if(mode==1){
                if(x==Gd)Gz=y;          //如果是多余的边就直接改
                else change(x,y);       //不然就另外改
            }else{
                int ans;
                ans=linkquery(x,y);         //第一种情况,不经过多余的边
                ans=min(ans,Gz+min(linkquery(x,Gx)+linkquery(Gy,y),linkquery(x,Gy)+linkquery(Gx,y)));   //第二种情况,经过多余的边
                printf("%d
    ",ans);
            }
        }
    }
    
  • 相关阅读:
    Git 使用vi或vim
    git 添加远程仓库后无法push
    windows下使用IIS创建git服务
    NPOI 操作office、word、excel
    delphi 模拟POST提交数据
    git 用远程覆盖本地
    Delphi中MD5实现方法(转)
    Delphi 操作Ini文件
    Spring系列之——spring security
    Spring系列之——使用模板快速搭建springboot项目
  • 原文地址:https://www.cnblogs.com/hyfhaha/p/10678150.html
Copyright © 2020-2023  润新知