zoukankan      html  css  js  c++  java
  • HDU-3699 Aragorn's Story(树链剖分+线段树)

    Our protagonist is the handsome human prince Aragorn comes from The Lord of the Rings. One day Aragorn finds a lot of enemies who want to invade his kingdom. As Aragorn knows, the enemy has N camps out of his kingdom and M edges connect them. It is guaranteed that for any two camps, there is one and only one path connect them. At first Aragorn know the number of enemies in every camp. But the enemy is cunning , they will increase or decrease the number of soldiers in camps. Every time the enemy change the number of soldiers, they will set two camps C1 and C2. Then, for C1, C2 and all camps on the path from C1 to C2, they will increase or decrease K soldiers to these camps. Now Aragorn wants to know the number of soldiers in some particular camps real-time.
    Input
    Multiple test cases, process to the end of input.

    For each case, The first line contains three integers N, M, P which means there will be N(1 ≤ N ≤ 50000) camps, M(M = N-1) edges and P(1 ≤ P ≤ 100000) operations. The number of camps starts from 1.

    The next line contains N integers A1, A2, …AN(0 ≤ Ai ≤ 1000), means at first in camp-i has Ai enemies.

    The next M lines contains two integers u and v for each, denotes that there is an edge connects camp-u and camp-v.

    The next P lines will start with a capital letter ‘I’, ‘D’ or ‘Q’ for each line.

    ‘I’, followed by three integers C1, C2 and K( 0≤K≤1000), which means for camp C1, C2 and all camps on the path from C1 to C2, increase K soldiers to these camps.

    ‘D’, followed by three integers C1, C2 and K( 0≤K≤1000), which means for camp C1, C2 and all camps on the path from C1 to C2, decrease K soldiers to these camps.

    ‘Q’, followed by one integer C, which is a query and means Aragorn wants to know the number of enemies in camp C at that time.
    Output
    For each query, you need to output the actually number of enemies in the specified camp.
    Sample Input
    3 2 5
    1 2 3
    2 1
    2 3
    I 1 3 5
    Q 2
    D 1 2 2
    Q 1
    Q 3
    Sample Output
    7
    4
    8

    Hint
    1.The number of enemies may be negative.

    2.Huge input, be careful.

    题意给出n个点,m条边,p次操作,建立一棵树.
    对于P次操作:
    输入I a,b,c 表示在树上的路径a到b上所有节点的值增长c
    输入D a,b,c表示树上路径a到b上所有节点的值减少c
    输入Q a 表示查询a节点的值

    树链剖分模板题
    可以用vector存图,用树状数组或线段树对树上路径进行区间更新。
    也可以用链式前向星存图,但就只能用树状数组更新,因为链式前向星至少是3倍vector存图的空间复杂度,如果再用线段树更新将会MLE超内存。

    关于树链剖分,因为我们无法直接对一棵树上的某些路径进行直接的区间更新,因此我们将树剖分为一条条单独的链,每一条链作为一段连续的区间存储,这样可以对区间路径上一些链进行整体的区间更新。

    剖分的过程需要两次DFS。
    我们首先要求出树链剖分操作所需要的几个数组:
    int fa[maxn];///每个节点的父节点,以形成树结构
    int deep[maxn];///每个节点的深度
    int top[maxn];///每条链的最上方第一个节点
    int son[maxn];///重儿子,每个节点的儿子中,子树最大的儿子
    int fp[maxn];///dfs链序的映射,和num数组表示的值相反
    int sum[maxn];///每颗子树的节点总结点数量,用于求重儿子
    int num[maxn];///每条链上连续的一段区间的序号,对于每条链,我们通过dfs序遍历得到这条链上各个节点的标号

    首先第一次dfs深搜可以求出每个节点的深度deep数组,可以算出每个子树的节点数量sum,可以根据sum得到重儿子son,可以得到每个节点的父节点。

    再通过第二次广搜得到每条链的top数组,每条链的第一个节点,再根据上一个dfs中计算得到的son的得到一整条链的遍历序。

    处理好树上的每个节点成为一条条链后,即可用树状数组线段树维护。
    而针对的每个节点的序号,即Num[]数组所存的值。在线段树上,我们建树时所需要的是在num序下的每个数的下标,而我们只根据Num只能得到原节点对应的num序,不知道num序对应的原节点值。此时用到了fp数组,根据fp数组,反向得到num序对应的原节点序,再将该值套用到val节点值的下标上,即可在建树时按一定顺序更新每个节点的值。

    最后我们进行更新操作,因为被分成了若干条链,比如我们想更新树上的某个节点至某个节点路径上的值,这两个节点分别位于不同的链,那么首先我们利用change函数,开始查询两个节点的top节点,是否相同,若相同说明在同一条链上,否则不在同一条链。
    若不在同一条链上,对比两条链的top节点深度,我们将先计算深度较深的链,对于这条链,直接更新其区间。然后上溯到该top节点的父节点,再次判断其父节点所在的链是否和另一节点在同一链上,继续更新和上溯。

    当两者在同一链上时,跳出循环,直接对该链的一段区间进行更新,这样就完成了整条路径的分段更新。

    代码如下:

    #include<stdio.h>
    #include<algorithm>
    #include<string.h>
    #include<vector>
    using namespace std;
    const int maxn=5e4+9;
    int n,m,q;
    int val[maxn];
    vector<int>mp[maxn];
    //int head[maxn],cnt;
    //struct edge///链式前向星
    //{
    //    int to,next;
    //}mp[maxn];
    //void add(int from,int to)
    //{
    //    mp[++cnt].next=head[from];
    //    mp[cnt].to=to;
    //    head[from]=cnt;
    //}
    int fa[maxn];///每个节点的父节点,以形成树结构
    int deep[maxn];///每个节点的深度
    int top[maxn];///每条链的最上方第一个节点
    int son[maxn];///重儿子,每个节点的儿子中,子树最大的儿子
    int fp[maxn];///dfs链序的映射,和num数组表示的值相反
    int sum[maxn];///每颗子树的节点总结点数量,用于求重儿子
    int num[maxn];///每条链上连续的一段区间的序号,对于每条链,我们通过dfs序遍历得到这条链上各个节点的标号
    int pos;
    void dfs1(int rt,int pre,int dep)
    {
        deep[rt]=dep;
        fa[rt]=pre;
        sum[rt]=1;
        for(int i=0;i<mp[rt].size();i++)
        {
            int v=mp[rt][i];
            if(v!=pre)
            {
                dfs1(v,rt,dep+1);
                sum[rt]+=sum[v];
                if(son[rt]==0||sum[v]>sum[son[rt]]) son[rt]=v;
            }
        }
    }
    void dfs2(int rt,int boss)
    {
        top[rt]=boss;
        num[rt]=++pos;
        fp[num[rt]]=rt;
        if(son[rt]==0) return;
        dfs2(son[rt],boss);
        for(int i=0;i<mp[rt].size();i++)
        {
            int v=mp[rt][i];
            if(v!=son[rt]&&v!=fa[rt])dfs2(v,v);///新开一条链
        }
    }
    struct node
    {
        int l,r,val,lazy;
    }tre[maxn<<2];
    void pushdown(int rt)
    {
        if(tre[rt].lazy)
        {
            tre[rt<<1].lazy+=tre[rt].lazy;
            tre[rt<<1|1].lazy+=tre[rt].lazy;
            tre[rt<<1].val+=(tre[rt<<1].r-tre[rt<<1].l+1)*tre[rt].lazy;
            tre[rt<<1|1].val+=(tre[rt<<1|1].r-tre[rt<<1|1].l+1)*tre[rt].lazy;
            tre[rt].lazy=0;
        }
        return ;
    }
    void build(int l,int r,int rt)
    {
        tre[rt].l=l;
        tre[rt].r=r;
        tre[rt].lazy=0;
        if(l==r)
        {
            tre[rt].val=val[fp[l]];
            return;
        }
        int mid=(l+r)>>1;
        build(l,mid,rt<<1);
        build(mid+1,r,rt<<1|1);
        tre[rt].val=tre[rt<<1].val+tre[rt<<1|1].val;
    }
    void update(int l,int r,int val,int rt)
    {
        if(tre[rt].l>=l&&tre[rt].r<=r)
        {
            tre[rt].val+=(tre[rt].r-tre[rt].l+1)*val;
            tre[rt].lazy+=val;
            return ;
        }
        pushdown(rt);
        int mid=(tre[rt].l+tre[rt].r)>>1;
        if(l<=mid)update(l,r,val,rt<<1);
        if(r>mid)update(l,r,val,rt<<1|1);
        tre[rt].val=tre[rt<<1].val+tre[rt<<1|1].val;
        return ;
    }
    int query(int pos,int rt)
    {
        if(tre[rt].l==pos&&tre[rt].r==pos)
            return tre[rt].val;
        pushdown(rt);
        int mid=(tre[rt].l+tre[rt].r)>>1;
        if(pos>mid) return query(pos,rt<<1|1);
        else if(pos<=mid) return query(pos,rt<<1);
    }
    
    void change(int l,int r,int val)
    {
        int f1=top[l],f2=top[r];
        while(f1!=f2)
        {
            if(deep[f1]<deep[f2])
            {
                swap(f1,f2);
                swap(l,r);
            }
            update(num[f1],num[l],val,1);
            l=fa[f1];
            f1=top[l];
        }
        if(deep[l]>deep[r]) swap(l,r);
        update(num[l],num[r],val,1);
    }
    int main()
    {
        while(scanf("%d%d%d",&n,&m,&q)!=EOF)
        {
            int from,to;
            pos=0;
            for(int i=0;i<=n;i++)mp[i].clear();
    //        memset(head,0,sizeof head);
            memset(son,0,sizeof son);
            for(int i=1;i<=n;i++)scanf("%d",&val[i]);
            for(int i=1;i<=m;i++)
            {
                scanf("%d%d",&from,&to);
                mp[from].push_back(to);
                mp[to].push_back(from);
    //            add(from,to);
    //            add(to,from);///双向边
            }
            dfs1(1,0,0);///根节点1开始搜,父节点为0,深度为0
            dfs2(1,1);///根节点开始搜,第一个top节点为根节点
            build(1,n,1);
    //        for(int i=1;i<=n;i++) update(num[i],num[i],val[i],1);
            char op[3];
            int l,r,k;
            while(q--)
            {
                scanf("%s",op);
                if(op[0]=='Q')
                {
                    scanf("%d",&k);
                    printf("%d
    ",query(num[k],1));
                }
                else
                {
                    scanf("%d%d%d",&l,&r,&k);
                    if(op[0]=='D')k=-k;
                    change(l,r,k);
                }
            }
        }
    }
    /*
    14 13 99
    1 2 3 4 5 6 7 8 9 10 11 12 13 14
    1 2
    1 3
    1 4
    2 5
    2 6
    6 11
    6 12
    3 7
    4 8
    4 9
    4 10
    9 13
    13 14
    Q 2
    I 5 8 1
    */
    
    
  • 相关阅读:
    算法-排序(二)-快速排序
    算法- 排序(一)
    python(十四)新式类和旧式类
    Python(十三)python的函数重载
    django(二)中间件与面向切面编程
    MySQL(二)MySQL的启动或链接失败
    django(一)验证码
    python(七) Python中单下划线和双下划线
    Python(十) Python 中的 *args 和 **kwargs
    python(六)列表推导式、字典推导式、集合推导式
  • 原文地址:https://www.cnblogs.com/kuronekonano/p/11135769.html
Copyright © 2011-2022 走看看