BZOJ4127 Abs (树链剖分 线段树)

题目大意

给出一棵带权有根树,要求完成以下几种操作:

1 u v d 表示将路径 (u,v) 加d (0<=d<=1e8)
2 u v 表示询问路径 (u,v) 上点权绝对值的和


题解

正常树剖,用线段树维护几个信息:绝对值的和区间内负数的个数区间内最大的负数 以及 最大的负数所在的位置,因为有区间加操作,所以还要维护一个加标记

这道题棘手的地方就在于,数字的正负性会随着区间加操作而改变,所以说不能直接维护区间和。所以说观察数据范围 (0<=d<=1e8) 可以得知这道题序列中的所有数字,只会从负变正而不会从正变负,所以说序列内数字的正负性最多只改变n次,所以对于每次数字正负性的变化暴力修改就可以了。

每次处理一个区间加操作之前,先检查区间内进行这个加操作之后是否会有数字的正负性发生改变,也就是说区间内最大的负数改变之后是否大于等于0。如果有的话递归改变那个位置元素的正负性更新线段数,然后再进行加操作。

其他操作与普通树剖线段数无异,祝大家1A。


代码

#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;

const int maxx=40000008;
char Input[maxx+5],*ipos;
#define read() (strtol(ipos,&ipos,10))

const int maxn=int(1e5)+11, inf=1<<30;
int n,m,v[maxn];
int tot=0,head[maxn];

struct Edge {
    int from,to,next;
    Edge() {}
    Edge(int x,int y,int nx):from(x),to(y),next(nx) {}
}eage[maxn*2];
inline void add(int x,int y) {
    eage[tot]=Edge(x,y,head[x]), head[x]=tot++;
    eage[tot]=Edge(y,x,head[y]), head[y]=tot++;
    return;
}

int fa[maxn],dep[maxn],siz[maxn],son[maxn];
inline void dfs1(register int u) {
    siz[u]=1, son[u]=0;
    for(register int i=head[u];~i;i=eage[i].next) if(eage[i].to!=fa[u]) {
        fa[eage[i].to]=u;
        dep[eage[i].to]=dep[u]+1;
        dfs1(eage[i].to);
        siz[u]+=siz[eage[i].to];
        if(siz[eage[i].to]>siz[son[u]]) son[u]=eage[i].to;
    }
}

int top[maxn],seq[maxn],id[maxn],ind;
int val[maxn];
inline void dfs2(register int u) {
    seq[++ind]=u, id[u]=ind;
    if(son[fa[u]]==u) top[u]=top[fa[u]];
    else top[u]=u;
    if(son[u]) dfs2(son[u]);
    register int i;
    for(i=head[u];~i;i=eage[i].next) if(eage[i].to!=fa[u] && eage[i].to!=son[u])
        dfs2(eage[i].to);
}

#define mp make_pair
#define ls(k) ((k)<<1)
#define rs(k) (ls(k)^1)
#define mid ((l+r)>>1)

struct Node {
    long long sum,add;
    int len,cnt;
    pair<int,int> pos;
    inline void get_add(long long);
    inline void maintain(const Node&,const Node&);
}node[maxn*4];

inline void Node:: get_add(long long a) {
    sum+=(len-2*cnt)*a, add+=a;
    if(pos.first!=-inf) pos.first+=a;
    return;
}
inline void pushdown(int k) {
    Node &ls=node[ls(k)], &rs=node[rs(k)];
    long long &add=node[k].add;
    if(add) {
        ls.get_add(add), rs.get_add(add);
        add=0;
    }
    return;
}
inline void Node:: maintain(const Node &a,const Node &b) {
    sum=a.sum+b.sum; add=0;
    len=a.len+b.len; cnt=a.cnt+b.cnt;
    pos=max(a.pos,b.pos);
}

inline void build(int k,int l,int r) {
    node[k].len=r-l+1;
    if(l==r) {
        node[k].sum=(int)abs(1.0*val[l]);
        if(val[l]>=0) node[k].pos=mp(-inf,0);
        else node[k].cnt=1, node[k].pos=mp(val[l],l);
        return;
    }
    build(ls(k),l,mid);
    build(rs(k),mid+1,r);
    node[k].maintain(node[ls(k)],node[rs(k)]);
    return;
}
void Build() {
    build(1,1,n);
    return;
}

inline void modify(int k,int l,int r,int ql,int qr,int val) {
    if(l!=r) pushdown(k);
    if(ql<=l && r<=qr) {
        node[k].get_add(val);
        return;
    }
    if(ql<=mid) modify(ls(k),l,mid,ql,qr,val);
    if(qr> mid) modify(rs(k),mid+1,r,ql,qr,val);
    node[k].maintain(node[ls(k)],node[rs(k)]);
    return;
}
void modify(int l,int r,int a) {
    modify(1,1,n,l,r,a);
    return;
}

inline void flip(int k,int l,int r,int pos) {
    if(l!=r) pushdown(k);
    if(l==r) {
        node[k].sum*=-1;
        node[k].cnt=0;
        node[k].pos=mp(-inf,0);
        return;
    }
    if(pos<=mid) flip(ls(k),l,mid,pos);
    if(pos> mid) flip(rs(k),mid+1,r,pos);
    node[k].maintain(node[ls(k)],node[rs(k)]);
    return;
}
void flip(int pos) {
    flip(1,1,n,pos);
    return;
}

inline pair<int,int> get_pos(int k,int l,int r,int ql,int qr) {
    if(l!=r) pushdown(k);
    if(ql<=l && r<=qr) return node[k].pos;
    pair<int,int> res=mp(-inf,0);
    if(ql<=mid) res=max(res,get_pos(ls(k),l,mid,ql,qr));
    if(qr> mid) res=max(res,get_pos(rs(k),mid+1,r,ql,qr));
    return res;
}
pair<int,int> get_pos(int l,int r) {
    return get_pos(1,1,n,l,r);
}

inline long long query(int k,int l,int r,int ql,int qr) {
    if(l!=r) pushdown(k);
    if(ql<=l && r<=qr) return node[k].sum;
    long long res=0;
    if(ql<=mid) res+=query(ls(k),l,mid,ql,qr);
    if(qr> mid) res+=query(rs(k),mid+1,r,ql,qr);
    return res;
}
long long Query(int l,int r) {
    return query(1,1,n,l,r);
}

inline void Modify(int l,int r,int val) {
    while(true) {
        pair<int,int> pos=get_pos(l,r);
        if(pos.first+val>=0) flip(pos.second);
        else break;
    }
    modify(l,r,val);
    return;
}

inline void Tree_modify(register int u,register int v,int a) {
    while(top[u]!=top[v]) {
        if(dep[top[u]]<dep[top[v]]) std::swap(u,v);
        Modify(id[top[u]],id[u],a);
        u=fa[top[u]];
    }
    if(dep[u]>dep[v]) std::swap(u,v);
    Modify(id[u],id[v],a);
    return;
}

inline long long Tree_query(int u,int v) {
    long long res=0;
    while(top[u]!=top[v]) {
        if(dep[top[u]]<dep[top[v]]) std::swap(u,v);
        res+=Query(id[top[u]],id[u]);
        u=fa[top[u]];
    }
    if(dep[u]>dep[v]) std::swap(u,v);
    res+=Query(id[u],id[v]);
    return res;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
#endif // ONLINE_JUDGE

    fread(Input,maxx,1,stdin);ipos=Input;
    n=read(), m=read();

    for(int i=1;i<=n;i++) {
        head[i]=-1;
        v[i]=read();
    }
    for(int x,y,i=1;i<n;i++) {
        x=read(), y=read();
        add(x,y);
    }
    dep[1]=1, fa[1]=-1;
    dfs1(1),
    dfs2(1);

    for(int i=1;i<=n;i++)
        val[i]=v[seq[i]];
    Build();

    int op,u,v,a;
    for(int i=1;i<=m;i++) {
        op=read(), u=read(), v=read();
        if(op==1) {
            a=read();
            Tree_modify(u,v,a);
        } else {
            printf("%lld\n",Tree_query(u,v));
        }
    }

    return 0;
}

数据生成器:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <ctime>
using namespace std;

const int n=int(1e5), m=n;
int maxdep=0;
bool vis[n+11];
int f[n+11];

inline int _find(register int k) {
    if(f[k]==k) return k;
    return f[k]=_find(f[k]);
}

int main() {
    freopen("input0.txt","w",stdout);
    srand(time(NULL));

    printf("%d %d\n",n,m);
    for(int i=1;i<=n;i++) {
        int sign=rand()&1;
        sign=sign?1:-1;
        printf("%d ",sign*rand());
    }
    printf("\n");

    for(int i=1;i<=n;i++)
        f[i]=i;
    int tot=0;
    while(tot<n-1) {
        int u=(1ll*rand()*rand())%n+1, v=(1ll*rand()*rand())%n+1;
        int g1=_find(u), g2=_find(v);
        if(g1!=g2) {
            f[g1]=g2;
            printf("%d %d\n",u,v);
            tot++;
        }
    }

    for(int i=1;i<=m;i++) {
        int op=rand()%2+1;
        int u=(1ll*rand()*rand())%n+1, v=(1ll*rand()*rand())%n+1, d=(1ll*rand()*rand())%((int)1e5)+1;
        if(op==1) printf("%d %d %d %d\n",op,u,v,d);
        else printf("%d %d %d\n",op,u,v);
    }

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值