【Bzoj4353】play with tree

题意

给你一棵包含N个节点的树,设每条边一开始的边权为0,现在有两种操作:
1)给出参数U,V,C,表示把U与V之间的路径上的边权变成C(保证C≥0)
2)给出参数U,V,C,表示把U与V之间的路径上的边权加上C。但是如果U至V之间路径某条边的边权加上C小于0,那么C=这条边的边权的相反数。
你需要统计出每次一操作过后树中边权为0的边有多少条。


解析

边权树链剖分。对于操作1直接修改,对于操作2,先检测树链上最小值,看要不要改C,然后再加。每次查的话如果Tn[i]=0就累加答案。注意事项的话就是合并的时候看两边最小值是否一样。还有修改和覆盖的关系要搞清。


#include <cstdio>
#include <algorithm>

#define Rep( i , _begin , _end ) for(int i=(_begin),i##_END=(_end);i<=(i##_END);i++)
#define For( i , _begin , _end ) for(int i=(_begin),i##_END=(_end);i!=(i##_END);i++)
#define Lop( i , _begin , _end ) for(int i=(_begin),i##_END=(_end);i>=(i##_END);i--)
#define Dnt( i , _begin , _end ) for(int i=(_begin),i##_END=(_end);i!=(i##_END);i--)

using std :: max;
using std :: min;

const int maxx = 200000 + 25;
const int Inf = (unsigned)(-1) >> 1;

typedef int Array[maxx];

int n,m,x,y,z,num,tot,ans,f;

int head[maxx],to[maxx<<1],nxt[maxx<<1],w[maxx<<1];
int Tn[maxx<<2],cnt[maxx<<2],Add[maxx<<2],st[maxx<<2];

Array top,size,ftr,son,dpt,rnk;

namespace Cute{

    void Ins(int x,int y){
        to[++num] = y;nxt[num] = head[x];head[x] = num;
    }

    void Dfs(int x){
        size[x] = 1;
        for(int i=head[x];i;i=nxt[i]){
            if(to[i] == ftr[x]) continue;
            dpt[to[i]] = dpt[x] + 1;
            ftr[to[i]] = x;
            Dfs(to[i]);size[x] += size[to[i]];
            if(size[to[i]] > size[son[x]]) son[x] = to[i];
        }
    }

    void __Dfs(int x,int brn){
        top[x] = brn;rnk[x] = ++tot;
        if(son[x]) __Dfs(son[x],brn);
        for(int i=head[x];i;i=nxt[i])
            if(to[i] != ftr[x] && to[i] != son[x])
                __Dfs(to[i],to[i]);
    }

}

namespace Segment_Tree{

    void upt(int i){
        if(Tn[i<<1] == Tn[i<<1|1]) {Tn[i] = Tn[i<<1];cnt[i] = cnt[i<<1] + cnt[i<<1|1];return;}
        Tn[i] = min(Tn[i<<1],Tn[i<<1|1]);
        cnt[i] = Tn[i<<1] < Tn[i<<1|1]? cnt[i<<1] : cnt[i<<1|1];
    }

    void Build(int i,int l,int r){
        st[i] = -1;
        if(l == r) {Tn[i] = 0;cnt[i] = 1;return;}
        int mid = (l+r) >> 1;
        Build(i<<1,l,mid);Build(i<<1|1,mid+1,r);
        upt(i);
    }

    void pushdown(int i,int l,int r){
        int mid = (l+r) >> 1;
        if(st[i] != -1){
            st[i<<1] = st[i];Add[i<<1] = 0;Tn[i<<1] = st[i];cnt[i<<1] = (mid-l+1);
            st[i<<1|1] = st[i];Add[i<<1|1] = 0;Tn[i<<1|1] = st[i];cnt[i<<1|1] = (r-mid);
            st[i] = -1;
        }
        if(Add[i]){
            Add[i<<1] += Add[i];Tn[i<<1] += Add[i];
            Add[i<<1|1] += Add[i];Tn[i<<1|1] += Add[i];
            Add[i] = 0;
        }
    }

    void modify(int i,int x,int y,int l,int r,int f,int k){
        if(x <= l && r <= y){
            if(f == 1) {Tn[i] += k;Add[i] += k;return;}
            if(f == 2) {st[i] = k;Tn[i] = k;Add[i] = 0;cnt[i] = (r-l+1);return;}
        }
        int mid = (l+r) >> 1;
        pushdown(i,l,r);
        if(x <= mid) modify(i<<1,x,y,l,mid,f,k);
        if(y >  mid) modify(i<<1|1,x,y,mid+1,r,f,k);
        upt(i);
    }

    int Query(int i,int x,int y,int l,int r){
        int ans = Inf;
        if(x <= l && r <= y) return Tn[i];
        int mid = (l+r) >> 1;
        pushdown(i,l,r);
        if(x <= mid) ans = min(ans,Query(i<<1,x,y,l,mid));
        if(y >  mid) ans = min(ans,Query(i<<1|1,x,y,mid+1,r));
        return ans;
    }

    int Getcnt(int i,int x,int y,int l,int r){
        if(x <= l && r <= y) return Tn[i]==0? cnt[i] : 0;
        int ans = 0;
        int mid = (l+r) >> 1;
        pushdown(i,l,r);
        if(x <= mid) ans += Getcnt(i<<1,x,y,l,mid);
        if(y >  mid) ans += Getcnt(i<<1|1,x,y,mid+1,r);
        return ans;
    }

}

namespace Sb_Linux{

    using namespace Segment_Tree;

    int Get(int x,int y){
        int ans = Inf;
        while(top[x] != top[y]){
            if(dpt[top[x]] > dpt[top[y]]) std :: swap(x,y);
            ans = min(ans,Query(1,rnk[top[y]],rnk[y],1,n));
            y = ftr[top[y]];
        }
        if(rnk[x] > rnk[y]) std :: swap(x,y);
        if(x != y) ans = min(ans,Query(1,rnk[x]+1,rnk[y],1,n));
        return ans;
    }

    void C(int x,int y,int f,int k){
        if(f == 1){
            int tmp = Get(x,y);
            if(tmp + k < 0) k = -tmp;
        }
        while(top[x] != top[y]){
            if(dpt[top[x]] > dpt[top[y]]) std :: swap(x,y);
            modify(1,rnk[top[y]],rnk[y],1,n,f,k);
            y = ftr[top[y]];
        }
        if(rnk[x] > rnk[y]) std :: swap(x,y);
        if(x != y) modify(1,rnk[x]+1,rnk[y],1,n,f,k);
    }

}

using namespace Cute;
using namespace Segment_Tree;
using namespace Sb_Linux;

int main(){
    scanf("%d%d",&n,&m);
    For( i , 1 , n ) scanf("%d%d",&x,&y),Ins(x,y),Ins(y,x);
    Dfs(1);__Dfs(1,1);
    Build(1,1,n);
    while( m-- ){
        scanf("%d%d%d%d",&f,&x,&y,&z);
        if(f == 1) C(x,y,2,z);
        if(f == 2) C(x,y,1,z);
        printf("%d\n",Getcnt(1,2,n,1,n)); 
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值