zoukankan      html  css  js  c++  java
  • Codeforces 838B

    题目链接:http://codeforces.com/problemset/problem/838/B

    You are given a directed weighted graph with n nodes and 2n - 2 edges. The nodes are labeled from 1 to n, while the edges are labeled from 1 to 2n - 2. The graph's edges can be split into two parts.

    • The first n - 1 edges will form a rooted spanning tree, with node 1 as the root. All these edges will point away from the root.
    • The last n - 1 edges will be from node i to node 1, for all 2 ≤ i ≤ n.

    You are given q queries. There are two types of queries

    • i w: Change the weight of the i-th edge to w
    • u v: Print the length of the shortest path between nodes u to v

    Given these queries, print the shortest path lengths.

    Input

    The first line of input will contain two integers n, q (2 ≤ n, q ≤ 200 000), the number of nodes, and the number of queries, respectively.

    The next 2n - 2 integers will contain 3 integers ai, bi, ci, denoting a directed edge from node ai to node bi with weight ci.

    The first n - 1 of these lines will describe a rooted spanning tree pointing away from node 1, while the last n - 1 of these lines will have bi = 1.

    More specifically,

    • The edges (a1, b1), (a2, b2), ... (an - 1, bn - 1) will describe a rooted spanning tree pointing away from node 1.
    • bj = 1 for n ≤ j ≤ 2n - 2.
    • an, an + 1, ..., a2n - 2 will be distinct and between 2 and n.

    The next q lines will contain 3 integers, describing a query in the format described in the statement.

    All edge weights will be between 1 and 106.

    Output

    For each type 2 query, print the length of the shortest path in its own line.

    Example

    Input
    5 9
    1 3 1
    3 2 2
    1 4 3
    3 5 4
    5 1 5
    3 1 6
    2 1 7
    4 1 8
    2 1 1
    2 1 3
    2 3 5
    2 5 2
    1 1 100
    2 1 3
    1 8 30
    2 4 2
    2 2 4
    Output
    0
    1
    4
    8
    100
    132
    10

    题意:

    给出n个节点,2n-2条边(有向带权);

    其中前n-1条边使得n个点构成一棵带权有向树;后n-1条边,权重为w,是从2~n号节点直接连接向1号节点的;

    现在给出两种操作:

    ①修改第 i 条边的权重为w;

    ②查询节点u和v之间最短路径的权重和;

    题解:

    先忽略后n-1条边,DFS序拍平整棵树;

    同时在DFS时,顺便计算出节点i的dist[i],代表从节点1到节点i的唯一的一条路径的权重和;

    记录每个节点的 dist[i] + Edge(i→1).weight,用线段树在DFS序上维护区间最小值;

    then……

    对于修改操作:

      ①若修改的边是Edge(x→1)类型的,那么更新区间[ in[x] , in[x] ]上的值;

      ②否则,更新区间[ in[x] , out[x] ]上的值;

    对于查询操作:

      ①v在u统领的子树内,按道理来讲直接dist[v]-dist[u]即可,

       但是我们在修改边权时不对dist[]数组进行更新,所以要通过式子dist[x] = query([ in[x] , in[x] ]) - Edge(x→1).weight来求得dist[u]和dist[v];

      ②v不在u统领的子树内,那么只能通过 u → … → 1 → … → v 这样的路径从u走到v,

       显然,u统领的子树内的每个节点,都能使得从节点u出发回到节点1,

       那么我们就query([ in[u] , out[u] ])查:u统领的这颗子树内的哪个节点,它的 dist[x] + Edge(x→1).weight 是最小的;

       一旦查到,ans = query([ in[u] , out[u] ]) - dist[u] + dist[v],同样的道理,这里的dist[u]和dist[v]都要像上面那样通过式子 dist[x] = query([ in[x] , in[x] ]) - Edge(x→1).weight 求得。

    AC代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int maxn=200000+10;
    const LL INF=1e18;
    
    int n,q;
    
    //邻接表
    struct Edge{
        int u,v;
        LL w;
        Edge(int u,int v,LL w)
        {
            this->u=u;
            this->v=v;
            this->w=w;
        }
    };
    vector<Edge> E; int E_size;
    vector<int> G[maxn];
    int ptr[2*maxn];
    void adjListInit(int l,int r)
    {
        E.clear(); E_size=0;
        for(int i=l;i<=r;i++) G[i].clear();
    }
    void addEdge(int u,int v,LL w,int i)
    {
        E.push_back(Edge(u,v,w)); E_size++;
        ptr[i]=E_size-1;
        G[u].push_back(E_size-1);
    }
    
    //存储所有返回到1的边(即编号为n~2n-2的边)
    vector<Edge> Eback;
    int Gback[maxn];
    int Eback_size;
    
    //DFS建立DFS序列,以及计算深度
    LL dist[maxn];
    int in[maxn],out[maxn];
    int peg[maxn];
    int dfs_clock;
    inline void dfsInit()
    {
        dist[1]=0;
        dfs_clock=0;
    }
    void dfs(int now,int par)
    {
        //printf("now=%d
    ",now);
        in[now]=++dfs_clock;
        peg[in[now]]=now;
    
        for(int i=0,_size=G[now].size();i<_size;i++)
        {
            Edge &e=E[G[now][i]]; int nxt=e.v;
            if(nxt!=par)
            {
                dist[nxt]=dist[now]+e.w;
                dfs(nxt,now);
            }
        }
    
        out[now]=dfs_clock;
    }
    
    //线段树
    struct Node{
        int l,r;
        LL val,lazy;
        void update(LL x)
        {
            val+=x;
            lazy+=x;
        }
    }node[4*maxn];
    void pushdown(int root)
    {
        if(node[root].lazy)
        {
            node[root*2].update(node[root].lazy);
            node[root*2+1].update(node[root].lazy);
            node[root].lazy=0;
        }
    }
    void pushup(int root)
    {
        node[root].val=min(node[root*2].val,node[root*2+1].val);
    }
    void build(int root,int l,int r)
    {
        node[root].l=l; node[root].r=r;
        node[root].val=0; node[root].lazy=0;
        if(l==r)
        {
            int p=peg[l]; //从DFS序中的位置逆向查节点编号
            node[root].val=dist[p]+Eback[Gback[p]].w;
        }
        else
        {
            int mid=l+(r-l)/2;
            build(root*2,l,mid);
            build(root*2+1,mid+1,r);
            pushup(root);
        }
    }
    void update(int root,int st,int ed,int val)
    {
        if(st>node[root].r || ed<node[root].l) return;
        if(st<=node[root].l && node[root].r<=ed) node[root].update(val);
        else
        {
            pushdown(root);
            update(root*2,st,ed,val);
            update(root*2+1,st,ed,val);
            pushup(root);
        }
    }
    LL query(int root,int st,int ed)
    {
        if(ed<node[root].l || node[root].r<st) return INF;
        if(st<=node[root].l && node[root].r<=ed) return node[root].val;
        else
        {
            pushdown(root);
            LL lson=query(root*2,st,ed);
            LL rson=query(root*2+1,st,ed);
            pushup(root);
            return min(lson,rson);
        }
    }
    
    
    int main()
    {
        cin>>n>>q;
    
        adjListInit(1,n);
        for(int i=1;i<=n-1;i++)
        {
            int a,b; LL c;
            scanf("%d%d%I64d",&a,&b,&c);
            addEdge(a,b,c,i);
        }
    
        dfsInit();
        dfs(1,-1);
        //for(int i=1;i<=n;i++) printf("in[%d]=%d  out[%d]=%d
    ",i,in[i],i,out[i]);
    
        Eback.clear(); Eback_size=0;
        for(int i=n,a,b,c;i<=2*n-2;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            Eback.push_back(Edge(a,b,c)); Eback_size++;
            ptr[i]=Eback_size-1;
            Gback[a]=Eback_size-1;
        }
        Eback.push_back(Edge(1,1,0)); Eback_size++;
        Gback[1]=Eback_size-1;
    
        build(1,1,n);
        for(int i=1,type;i<=q;i++)
        {
            scanf("%d",&type);
            if(type==1)
            {
                int id; LL w;
                scanf("%d%I64d",&id,&w);
                if(id<n)
                {
                    Edge &e=E[ptr[id]];
                    //printf("Edge %d: %d->%d = %I64d
    ",id,e.u,e.v,e.w);
                    update(1,in[e.v],out[e.v],w-e.w);
                    e.w=w;
                }
                else
                {
                    Edge &e=Eback[ptr[id]];
                    //printf("Edge %d: %d->%d = %I64d
    ",id,e.u,e.v,e.w);
                    update(1,in[e.u],in[e.u],w-e.w);
                    e.w=w;
                }
            }
            if(type==2)
            {
                int u,v;
                scanf("%d%d",&u,&v);
                if(in[u]<=in[v] && in[v]<=out[u]) //v在u统领的子树内
                {
                    //printf("%d是%d的祖先
    ",u,v);
                    LL du=query(1,in[u],in[u])-Eback[Gback[u]].w;
                    LL dv=query(1,in[v],in[v])-Eback[Gback[v]].w;
                    printf("%I64d
    ",dv-du);
                }
                else
                {
                    //printf("%d不是%d的祖先
    ",u,v);
                    LL ans=query(1,in[u],out[u]);
    
                    LL du=query(1,in[u],in[u])-Eback[Gback[u]].w;
                    LL dv=query(1,in[v],in[v])-Eback[Gback[v]].w;
                    ans-=du;
                    ans+=dv;
    
                    printf("%I64d
    ",ans);
                }
            }
        }
    }

    PS.第一次200+行的代码1A,还是值得小小窃喜一下的

  • 相关阅读:
    lipo命令
    百度地图整合 手动
    Pos管理类库的第三方库
    css3实现各种渐变效果,比较适合做手机触屏版
    a标签加绝对定位在图片上面,a的链接和块状属性block失效,而且是所有IE版本都失效的
    一个好玩的jq+php实现转盘抽奖程序
    一个很简单的jQuery插件实例教程(菜鸟级)
    导航条固定在顶部
    网站上经常使用的第一次打开弹出广告特效
    一个超级简单php的留言板
  • 原文地址:https://www.cnblogs.com/dilthey/p/9005129.html
Copyright © 2011-2022 走看看