zoukankan      html  css  js  c++  java
  • 【BZOJ-4127】Abs 树链剖分 + 线段树 (有趣的姿势)

    4127: Abs

    Time Limit: 40 Sec  Memory Limit: 256 MB
    Submit: 381  Solved: 132
    [Submit][Status][Discuss]

    Description

    给定一棵树,设计数据结构支持以下操作

    1 u   v d  表示将路径 (u,v) 加d

    2 u v 表示询问路径 (u,v) 上点权绝对值的和

    Input

    第一行两个整数n和m,表示结点个数和操作数

    接下来一行n个整数a_i,表示点i的权值

    接下来n-1行,每行两个整数u,v表示存在一条(u,v)的边

    接下来m行,每行一个操作,输入格式见题目描述 

    Output

    对于每个询问输出答案

    Sample Input

    4 4
    -4 1 5 -2
    1 2
    2 3
    3 4
    2 1 3
    1 1 4 3
    2 1 3
    2 3 4

    Sample Output

    10
    13
    9

    HINT

    对于100%的数据,n,m <= 10^5 且 0<= d,|a_i|<= 10^8

    Source

    Solution

    树链剖分显然,把树上路径问题转化为序列问题

    然后线段树维护区间权值绝对值和,支持区间加

    注意Delta>=0这个条件,即1操作保证加数不为负,即实际值不发生减小

    于是线段树维护一些东西:

    l,r左右端点;maxf区间最大负数;num区间正数个数-负数个数;tag区间加的标记;sum区间绝对值和

    maxf的意义在于,对于区间加Delta,那么如果maxf+Delta<0很显然1操作后会出现变号的情况,对于维护绝对值和必然会做出影响,所以用来进行判断

    num的意义在于计算sum的变化,这里同样可以考虑维护正数个数和负数个数,但Code起来比较不方便

    tag的意义在于,如果区间+Delta不发生变号情况(即maxf+Delta<0||maxf>=0)的时候,可以直接打上标记,否则则需要把标记下放至叶节点,在向上更新答案

    Code

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int read()
    {
        int x=0,f=1; char ch=getchar();
        while (ch<'0' || ch>'9') {if (ch=='-') f=-1; ch=getchar();}
        while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();}
        return x*f;
    }
    #define maxn 110000
    int n,m,a[maxn];
    struct Edgenode{int to,next;}edge[maxn<<1];
    int head[maxn],cnt=1;
    void add(int u,int v){cnt++; edge[cnt].to=v; edge[cnt].next=head[u]; head[u]=cnt;}
    void insert(int u,int v){add(u,v); add(v,u);}
    //----------------------------------------------------------------------------------
    int size[maxn],fa[maxn],deep[maxn],son[maxn],pl[maxn],sz,pre[maxn],top[maxn],pr[maxn];
    void dfs_1(int now)
    {
        size[now]=1;
        for (int i=head[now]; i; i=edge[i].next)
            if (edge[i].to!=fa[now])
                {
                    fa[edge[i].to]=now;
                    deep[edge[i].to]=deep[now]+1;
                    dfs_1(edge[i].to);
                    if (size[son[now]]<size[edge[i].to]) son[now]=edge[i].to;
                    size[now]+=size[edge[i].to];
                }
    }
    void dfs_2(int now,int chain)
    {
        pl[now]=++sz; pre[sz]=a[now]; top[now]=chain;
        if (son[now]) dfs_2(son[now],chain);
        for (int i=head[now]; i; i=edge[i].next)
            if (edge[i].to!=son[now] && edge[i].to!=fa[now])
                dfs_2(edge[i].to,edge[i].to);
        pr[now]=sz;
    }
    //----------------------------------------------------------------------------------
    struct TreeNode
    {
        int l,r;long long maxf,tag,sum,num;
        void Add(int k) 
            {
                if (k<0) maxf=k,sum=-k,num=-1;
                    else maxf=0,sum=k,num=1;
                tag=0;
            }
    }tree[maxn<<2];
    long long Maxf(long long x,long long y)
    {
        if (x>=0 && y>=0) return 0;
        if (x>=0 || y>=0) return min(x,y); 
        return max(x,y);
    }
    void Update(int now)
    {
        tree[now].maxf=Maxf(tree[now<<1].maxf,tree[now<<1|1].maxf);
        tree[now].sum=tree[now<<1].sum+tree[now<<1|1].sum;
        tree[now].num=tree[now<<1].num+tree[now<<1|1].num;
    }
    void BuildTree(int now,int l,int r)
    {
        tree[now].l=l,tree[now].r=r;
        if (l==r) {tree[now].Add(pre[l]); return;}
        int mid=(l+r)>>1;
        BuildTree(now<<1,l,mid); BuildTree(now<<1|1,mid+1,r);
        Update(now);
    }
    void Pushdown(int now)
    {
        if (!tree[now].tag || tree[now].l==tree[now].r) return;
        int tag=tree[now].tag; tree[now].tag=0; 
        tree[now<<1].maxf+=tag; tree[now<<1].sum+=tree[now<<1].num*tag; tree[now<<1].tag+=tag;
        tree[now<<1|1].maxf+=tag; tree[now<<1|1].sum+=tree[now<<1|1].num*tag; tree[now<<1|1].tag+=tag;
    }
    void Change(int now,int L,int R,int D)
    {
        int l=tree[now].l,r=tree[now].r;
        if (L<=l && R>=r && (tree[now].maxf>=0 || tree[now].maxf+D<0)) 
            {tree[now].maxf+=D; tree[now].sum+=(long long)tree[now].num*D; tree[now].tag+=D; return;}
        if (l==r) {tree[now].Add(tree[now].maxf+D); return;}
        Pushdown(now);
        int mid=(l+r)>>1;
        if (L<=mid) Change(now<<1,L,R,D);
        if (R>mid) Change(now<<1|1,L,R,D);
        Update(now);
    }
    long long Query(int now,int L,int R)
    {    
        Pushdown(now);
        int l=tree[now].l,r=tree[now].r;
        if (L<=l && R>=r) return tree[now].sum;    
        int mid=(l+r)>>1; long long re=0;
        if (L<=mid) re+=Query(now<<1,L,R);
        if (R>mid) re+=Query(now<<1|1,L,R);
        return re;
    }
    void DeBug(int now)
    {
        int l=tree[now].l,r=tree[now].r;
        if (l==r) {printf("l==r=%d   Val=%d  maxf=%lld   tag=%lld   sum=%lld  num=%lld
    ",l,a[l],tree[now].maxf,tree[now].tag,tree[now].sum,tree[now].num);return;}
        int mid=(l+r)>>1;
        DeBug(now<<1); DeBug(now<<1|1); 
    }
    //----------------------------------------------------------------------------------
    void Solve_1(int x,int y,int D)
    {
        while (top[x]!=top[y])
            {
                if (deep[top[x]]<deep[top[y]]) swap(x,y);
                Change(1,pl[top[x]],pl[x],D);
                x=fa[top[x]];
            }
        if (deep[x]>deep[y]) swap(x,y);
        Change(1,pl[x],pl[y],D);
    }
    long long Solve_2(int x,int y)
    {
        long long re=0;
        while (top[x]!=top[y])
            {
                if (deep[top[x]]<deep[top[y]]) swap(x,y);
                re+=Query(1,pl[top[x]],pl[x]);
                x=fa[top[x]]; 
            }
        if (deep[x]>deep[y]) swap(x,y);
        re+=Query(1,pl[x],pl[y]);
        return re;
    }
    //----------------------------------------------------------------------------------
    int main()
    {
    //    freopen("4127.in","r",stdin);
    //    freopen("4127.out","w",stdout);
        n=read(),m=read();
        for (int i=1; i<=n; i++) a[i]=read();
        for (int u,v,i=1; i<=n-1; i++) u=read(),v=read(),insert(u,v);
        dfs_1(1); dfs_2(1,1); BuildTree(1,1,n);
        int opt,u,v,w;
        while (m--)
            {
                opt=read();
            //    DeBug(1); 
                if (opt==1) u=read(),v=read(),w=read(),Solve_1(u,v,w);
                else u=read(),v=read(),printf("%lld
    ",Solve_2(u,v));
            }
        return 0;
    }

    友情附送数据生成器:(Designed by YveH)

    #include<ctime>
    #include<cstdio>
    #include<cstdlib>
    using namespace std;
    int main()
    {
        freopen("4127.in","w",stdout);
        srand(time(0));
        int n=1000,q=10000;
        printf("%d %d
    ",n,q);
        for (int i=1;i<=n;i++)
            printf("%d ",rand()%10000-5000);
        printf("
    ");
        for (int i=2;i<=n;i++)
            printf("%d %d
    ",i,rand()%(i-1)+1);
        for (int i=1;i<=q;i++)
        {
            int opt=rand()%2+1;
            printf("%d ",opt);
            if (opt==1)
                printf("%d %d %d
    ",rand()%n+1,rand()%n+1,rand()%10000);
            if (opt==2)
                printf("%d %d
    ",rand()%n+1,rand()%n+1);
        }
        return 0;
    }
    数据生成器

    友情附送对拍:(Designed by YveH)

    #include<iostream>
    #include<cstdio>
    #include<windows.h>
    using namespace std;
    int main()
    {
        while (1)
        {
            system("4127data.exe");
            system("4127.exe");
            system("4127STD.exe");
            if (system("fc 4127.out 4127std.out"))
                break;
        }
        return 0;
    }
    对拍

    这道破题,两天前YveH和Etienne写了半天多,DCrusher大爷嘲讽他们,我替他们不服,然后自己果断写了1小时,然后调了3小时....发现自信写不错的链剖少打了一句话MDZZ

    (加上省队集训,第三题暴力打到70%就去吃饭了,回来懒得打了,体验了连续滚粗的快感)

    发现自己的常数已经接近Etienne了...就慢个100ms不到

     

  • 相关阅读:
    实战(三):对游戏的破解“木叶忍者”
    实战(一):对“钉钉”的逆向(实现打卡功能)
    实战(二):对“微信”的逆向(实现界面自定义)
    iOS签名机制
    AM64汇编
    动态调试(二)
    动态调试(一)
    theos(二)
    murmurhash
    虚继承
  • 原文地址:https://www.cnblogs.com/DaD3zZ-Beyonder/p/5522148.html
Copyright © 2011-2022 走看看