bnuoj 39566 Do use segment tree(树链剖分)

题目链接

给一棵树,有两种操作:

1 将a->b路径上的点权值改为c;

2 求a->b路径上的最大连续区间和。

思路:树链剖分。通过不断合并区间,维护区间最大左区间和、最大右区间和、区间最大连续和、区间和即可。

//#pragma comment(linker, "/STACK:10240000000,10240000000")
#include<iostream>
#include<stdio.h>
#include<math.h>
#include <string>
#include<string.h>
#include<map>
#include<queue>
#include<set>
#include<utility>
#include<vector>
#include<algorithm>
#include<stdlib.h>
using namespace std;
#define eps 1e-8
#define pii pair<int,int>
#define inf 0x3f3f3f3f
#define rd(x) scanf("%d",&x)
#define rd2(x,y) scanf("%d%d",&x,&y)
#define ll long long int
#define mod 1000000007
//#define maxn 200005
#define maxm 1000005
const int maxn=200100;
int ma(int a,int b){return a>b?a:b;}
struct Ed{
    int to,next;
}edge[maxn*4];
int head[maxn],pos;
int top[maxn];
int fa[maxn];
int deep[maxn];
int num[maxn];
int p[maxn];
int fp[maxn];
int d,tot;
int son[maxn];
void addedge(int u,int v){
    edge[tot].to=v;edge[tot].next=head[u];head[u]=tot++;
}
/*dfs和build_tree函数为将树映射到线段树上的初始化函数*/
void dfs(int u) {
    deep[u] = d;
    num[u] = 1;
    son[u] = 0;
    d++;
    for (int i = head[u]; ~i; i = edge[i].next) {
        if (edge[i].to != fa[u]) {
            fa[edge[i].to] = u;
            dfs(edge[i].to);
            num[u] += num[edge[i].to];
            if (son[u] == 0 || num[edge[i].to] > num[son[u]]) son[u] = edge[i].to;
        }
    }
    d--;
}

void build_tree(int u, int tp) {
    p[u] = pos++;  top[u] = tp;
    if (son[u] > 0) build_tree(son[u], tp);
    for (int i = head[u]; ~i; i = edge[i].next) {
        if (edge[i].to != fa[u] && edge[i].to != son[u])
            build_tree(edge[i].to, edge[i].to);
    }
}

void init(){
    tot=0;pos=0;
    memset(head,-1,sizeof(head));
    memset(son,-1,sizeof(son));
}
struct node{
    int l,r,ml,mr,ms,k,v,s;//k为覆盖标记
}tree[maxn*4];
int a[maxn],b[maxn];
node max_node(node ls,node rs){//求两个区间合并后的信息,
    node ans;                  //最大左连续值,最大右连续值和区间最大连续值h和区间和
    ans.k=0;
    ans.l=ls.l;ans.r=rs.r;
    ans.ml=ma(ls.ml,ls.s+rs.ml);
    ans.mr=ma(rs.mr,rs.s+ls.mr);
    ans.s=ls.s+rs.s;
    ans.ms=ma(ls.ms,ma(rs.ms,ls.mr+rs.ml));
    return ans;
}
void build(int i,int l,int r){
    tree[i].l=l;tree[i].r=r;
    tree[i].k=0;
    if(l==r) {
          tree[i].s=tree[i].ml=tree[i].mr=tree[i].ms=b[l];
            return;
    }
    int mid=(l+r)>>1;
    build(i<<1,l,mid);
    build((i<<1)|1,mid+1,r);
    tree[i]=max_node(tree[i*2],tree[i*2+1]);
}
void push_down(int i){
    int ls=i*2;
    int rs=i*2+1;
    if(tree[i].k){
        tree[ls].k=tree[rs].k=1;
        tree[ls].v=tree[rs].v=tree[i].v;
        tree[ls].s=(tree[ls].r-tree[ls].l+1)*tree[i].v;
        tree[rs].s=(tree[rs].r-tree[rs].l+1)*tree[i].v;
        if(tree[i].v>=0){
            tree[ls].ml=tree[ls].mr=tree[ls].ms=tree[ls].s;
            tree[rs].ml=tree[rs].mr=tree[rs].ms=tree[rs].s;
        }
        else{
            tree[ls].ml=tree[ls].mr=tree[ls].ms=tree[i].v;
            tree[rs].ml=tree[rs].mr=tree[rs].ms=tree[i].v;
        }
    }
    tree[i].k=0;
}
void update(int i,int l,int r,int val){
    if(tree[i].l==l&&tree[i].r==r){
      tree[i].k=1;
      tree[i].v=val;
      tree[i].s=val*(r-l+1);
      if(val>0) {
        tree[i].ml=tree[i].mr=tree[i].ms=tree[i].s;
      }
      else{
            tree[i].ml=tree[i].mr=tree[i].ms=val;
      }
        return;
    }
    push_down(i);
    int mid=(tree[i].l+tree[i].r)>>1;
    if(mid>=r) update(i<<1,l,r,val);
    else if(l>mid) update((i<<1)|1,l,r,val);
    else{
        update(i<<1,l,mid,val);
        update((i<<1)|1,mid+1,r,val);
    }
    tree[i]=max_node(tree[i*2],tree[i*2+1]);
}
void change(int u,int v,int val){
    int f1=top[u],f2=top[v];

    while(f1!=f2){
        if(deep[f1]<deep[f2]){
            swap(u,v);
          swap(f1,f2);
        }
        update(1,p[f1],p[u],val);
        u=fa[f1];f1=top[u];
    }

    if(deep[u]>deep[v]) {
            swap(u,v);
    }
    update(1,p[u],p[v],val);
}
node query(int i,int l,int r){
    if(tree[i].l==l&&tree[i].r==r){
        return tree[i];
    }
    push_down(i);
    int mid=(tree[i].l+tree[i].r)>>1;
     if(mid>=r) return query(i<<1,l,r);
    else if(l>mid) return query((i<<1)|1,l,r);
    else{
        node ls=query(i<<1,l,mid);
        node rs=query((i<<1)|1,mid+1,r);
        return max_node(ls,rs);
    }
}
int query(int u,int v){//求路径上的最大连续区间,不断合并被剖分成的区间维护信息集合,注意合并方向
    int f1=top[u],f2=top[v];
   int k1=0,k2=0;
   node ans1,ans2;
    while(f1!=f2){
       /* if(deep[f1]<deep[f2]){
            swap(f1,f2);
            swap(u,v);
        }
        update(1,p[f1],p[u],val);
        u=fa[f1];f1=top[u];*/
       if(deep[f1]>=deep[f2]){
        node aa=query(1,p[f1],p[u]);
        u=fa[f1];f1=top[u];
        if(k1) ans1=max_node(aa,ans1);
        else { k1=1; ans1=aa;
        }
       }
       else{
        node aa=query(1,p[f2],p[v]);
        v=fa[f2];f2=top[v];
        if(k2) ans2=max_node(aa,ans2);
        else { k2=1; ans2=aa;
        }
       }
    }
   // if(deep[u]>deep[v]) swap(u,v);
   // update(1,p[u],p[v],val);
   if(deep[u]<=deep[v]){
    node aa=query(1,p[u],p[v]);
    if(k2) ans2=max_node(aa,ans2);
        else { k2=1; ans2=aa;
        }
   }
   else{
    node aa=query(1,p[v],p[u]);
    if(k1) ans1=max_node(aa,ans1);
        else { k1=1; ans1=aa;
        }
   }
   if(k1&&k2){
    swap(ans1.ml,ans1.mr);
    node aa=max_node(ans1,ans2);
    return aa.ms;
   }
   else if(k1) return ans1.ms;
   else return ans2.ms;
}
int n,q,u,v,z,op;
int main()
{
   // rd(t);
    while(~rd2(n,q)){
        //rd(n);
        init();
        for(int i=1;i<=n;i++){
            rd(a[i]);
        }
        for(int i=1;i<n;i++){
            rd2(u,v);
            addedge(u,v);
            addedge(v,u);
        }
       // dfs1(1,0,0);
       // getpos(1,1);
       d=1;
       dfs(1);
       build_tree(1,1);
        for(int i=1;i<=n;i++){
            b[p[i]]=a[i];
        }
        build(1,0,n-1);
        for(int i=1;i<=q;i++){
            rd2(op,u);rd2(v,z);
            if(op==1) change(u,v,z);
            else{
                printf("%d\n",query(u,v));
            }

        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值