• POJ


    题意:有一棵树,每条边给定初始权值。一个人从s点出发。支持两种操作:修改一条边的权值;求从当前位置到点u的最短路径。

    分析:就是在边可以修改的情况下求树上最短路。如果不带修改的话,用RMQ预处理LCA即可。

    在静态版本的LCA问题上,用树状数组维护一条边在dfs序中表示的一段区间。为什么是一段区间,因为求该边之下的任意一点到根节点的距离都必须经过这条边。

    用树状数组差分前缀和的方式维护区间修改。

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    using namespace std;
    #define maxn 100005
    struct Edge{
        int to,next,id;
    }edge[maxn<<1];
     
    int n,a[maxn],head[maxn],dep[maxn<<1],cnt,pos[maxn],dfs_seq[maxn<<1],dfn,f[maxn<<1][20];
    int W[maxn],L[maxn],R[maxn],dfs_clock,C[maxn],G[maxn];
    
    inline void add(int u,int v,int id)
    {
        edge[cnt].to=v;
        edge[cnt].next=head[u];
        edge[cnt].id=id;
        head[u]=cnt++;
    }
     
    inline int lowbit(int x){return (x)&(-x);}
     
    void init()
    {
        memset(head,-1,sizeof(head));
        memset(pos,-1,sizeof(pos));
        memset(C,0,sizeof(C));
        cnt=dfn=0;
        dfs_clock=0;
    }
     
    void dfs(int u,int deep)
    {
        dfs_seq[dfn]=u,dep[dfn]=deep,pos[u]=dfn++;
        L[u]=++dfs_clock;
        for(int i=head[u];~i;i=edge[i].next){
            int v=edge[i].to;
            if(pos[v]==-1){
                G[edge[i].id]=v;
                dfs(v,deep+1);
                dfs_seq[dfn]=u,dep[dfn++]=deep;
            }
        }
        R[u]=dfs_clock;
    }
     
    void init_RMQ(int n)
    {
        for(int i=0;i<=n;++i) f[i][0]=i;
        for(int j=1;(1<<j)<=n;++j)
            for(int i=0;i+(1<<j)-1<=n;++i){
                if(dep[f[i][j-1]]<dep[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];
            }
    }
     
    inline int RMQ(int L,int R)
    {
        int k=0;
        while(1<<(k+1)<=R-L+1) ++k;
        if(dep[f[L][k]]<dep[f[R-(1<<k)+1][k]]) return f[L][k];
        return f[R-(1<<k)+1][k];
    }
     
    inline int lca(int u,int v)
    {
        if(pos[u]>pos[v]) return dfs_seq[RMQ(pos[v],pos[u])];
        return dfs_seq[RMQ(pos[u],pos[v])];
    }
     
    inline void update(int i,int x)
    {
        for(;i<=n;i+=lowbit(i)) C[i]+=x;
    }
     
    inline int sum(int i)
    {
        int s=0;
        for(;i>0;i-=lowbit(i)) s+=C[i];
        return s;
    }
     
    int main()
    {
        int i,u,v,k,q,w,s;
        while(~scanf("%d%d%d",&n,&q,&s)){
            init();
            for(i=1;i<n;++i){
                scanf("%d%d%d",&u,&v,&w);
                add(u,v,i);
                add(v,u,i);
                W[i]=w;
            }
            dfs(1,0);
            init_RMQ(dfn-1);
            u=s;
    
            for(i=1;i<n;++i){
                update(L[G[i]],W[i]);
                update(R[G[i]]+1,-W[i]);
            }
    
            while(q--){
                scanf("%d",&k);
                if(k){
                    scanf("%d%d",&u,&w);
                    update(L[G[u]],w-W[u]);
                    update(R[G[u]]+1,-w+W[u]);
                    W[u]=w;
                }
                else{
                    scanf("%d",&v);
                    printf("%d
    ",sum(L[s])+sum(L[v])-2*sum(L[lca(s,v)]));
                    s=v;
                }
            }
        }
        return 0;
    }

    树链剖分的方法也类似。剖出轻重链之后,可以快速求解lca,并用树状数组维护边的信息,同样是用差分的方式。

    通常树链剖分维护的是树上点的信息,但本题可以用每条边在dfs树上的后继点来表示该边。

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int maxn =1e5+5;
    struct Edge{
        int to,next;
    }E[2*maxn];
    int n,head[maxn],tot;
    int idx,size[maxn],fa[maxn],son[maxn],dep[maxn],top[maxn],l[maxn],r[maxn];
    int edge[maxn][3];
    int bit[maxn];
    
    void init()
    {   
        idx=tot=0;
        memset(head,-1,sizeof(head));
        dep[1]=0,fa[1]=1,size[0]=0;
        memset(son,0,sizeof(son));
    }
    void AddEdge(int u,int v)
    {
        E[tot] = (Edge){v,head[u]};
        head[u]=tot++;
    }
    void dfs1(int u)
    {
        size[u]=1;
        for(int i=head[u];~i;i=E[i].next){
            int v=E[i].to;
            if(v!=fa[u]){
                fa[v]=u;
                dep[v]=dep[u]+1;
                dfs1(v);
                size[u]+=size[v];
                if(size[son[u]]<size[v]) son[u]=v;
            }
        }
    }
    
    void dfs2(int u,int topu)
    {
        top[u]= topu;
        l[u] = ++idx;
        if(son[u]) dfs2(son[u],top[u]);
        for(int i=head[u];~i;i=E[i].next){
            int v=E[i].to;
            if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
        }
        r[u] = idx;
    }
    
    void add(int pos,int val){
        for(int i=pos;i<=n;i+= i&(-i)) bit[i]+=val;
    }
    
    inline int sum(int pos){
        int res=0;
        for(int i=pos;i;i-= i&(-i)) res+=bit[i];
        return res;
    }
    
    int lca(int u,int v){
        while(top[u]!=top[v]){
            if(dep[top[u]]<dep[top[v]]) swap(u,v);
            u = fa[top[u]];
        }
        return dep[u]<dep[v] ? u:v;
    }
    
    int dist(int u,int v){
        return sum(l[u])+sum(l[v]) - 2*sum(l[lca(u,v)]);
    }
    
    int main()
    {
        #ifndef ONLINE_JUDGE
            freopen("in.txt","r",stdin);
            freopen("out.txt","w",stdout);
        #endif
        int q,s,op,u,v,w;
        while(scanf("%d%d%d",&n,&q,&s)==3){
            init();
            memset(bit,0,sizeof(bit));
            for(int i=1;i<n;++i){
                scanf("%d%d%d",&u,&v,&w);
                AddEdge(u,v);
                AddEdge(v,u);
                edge[i][0] = u,edge[i][1] =v,edge[i][2] =w;
            }        
            dfs1(1);
            dfs2(1,1);
            for(int i=1;i<n;++i){
                if(dep[edge[i][0]]>dep[edge[i][1]]) swap(edge[i][0],edge[i][1]);
                v = edge[i][1];
                add(l[v],edge[i][2]);
                add(r[v]+1,-edge[i][2]);
            }
    
            while(q--){
                scanf("%d",&op);
                if(op==0){
                    scanf("%d",&u);
                    printf("%d
    ",dist(s,u));
                    s = u;
                }
                else{
                    int k;
                    scanf("%d%d",&k,&w);
                    v = edge[k][1];
                    add(l[v],w-edge[k][2]);
                    add(r[v]+1,-w+edge[k][2]);
                    edge[k][2] =w;
                }
            }
        }
        return 0;
    }
    为了更好的明天
  • 相关阅读:
    操作系统进程调度策略
    runnable & callable
    leetcode 124 二叉树中的最大路径和
    leetcode 24 两两交换链表中的节点
    leetcode 93 复原IP地址
    C++ 11 move
    leetcode 64 最小路径和
    leetcode 1143 最长公共子序列
    leetcode 528 按权重随机选择
    数据挖掘面试题(1)
  • 原文地址:https://www.cnblogs.com/xiuwenli/p/9492096.html
Copyright © 2020-2023  润新知