Aizu 2450 Do use segment tree (树链剖分)

本文介绍了一个结合树链剖分和线段树技术解决区间最大连续子序列和问题的实例。通过详细展示代码实现过程,包括初始化、更新操作及查询处理等关键步骤,为读者提供了一个完整的解决方案。

Aizu 2450 Do use segment tree


树链剖分 + 线段树区间更新,就是用线段树求区间最大连续子序列和。

求区间最大连续子序列和  的  区间更新   都很好实现。

要注意的是最后询问的时候多个区间合并的顺序和正反的问题(就是后面swap那儿)

一开始线段树初始化那儿写残了调了好久  -  -


--------

孙大大的板子好长啊 -   -


#include <bits/stdc++.h>
#define lson num<<1
#define rson num<<1|1
#define gl l,m,lson
#define gr m+1,r,rson
#define PARA int l=L,int r=R,int num=1
using namespace std;
const int MAXN = 2e5+100;
const int INF = 0x2f2f2f2f;

struct Node
{
    long long lmx,rmx,mx,sum,tag;
    long long l,r;
    void init(int a,int b)
    {
        l=a,r=b;
        tag=INF;
    }
    void mark(long long v)
    {
        tag=v;
        sum=v*(r-l+1);
        mx=lmx=rmx=max(v,sum);
    }
    void merge(Node a, Node b)
    {
        lmx=max(max(a.lmx,a.sum),max(a.sum+b.lmx,a.sum+b.sum));
        rmx=max(max(b.rmx,b.sum),max(b.sum+a.rmx,a.sum+b.sum));
        sum=a.sum+b.sum;
        mx=max(max(max(lmx,rmx),max(a.mx,b.mx)),a.rmx+b.lmx);
    }
    void swap()
    {
        ::swap(lmx,rmx);
    }
};

int L,R;
struct SegTree
{
    int num[MAXN];
    Node st[MAXN<<2];
    void pushUp(int num)
    {
        st[num].merge(st[lson],st[rson]);
    }
    void pushDown(int num)
    {
        long long &v=st[num].tag;
        int l=st[num].l,r=st[num].r;
        if(l!=r&&v!=INF)
        {
            st[lson].mark(v);
            st[rson].mark(v);
            pushUp(num);
        }
        v=INF;
    }
    void init(int v[],PARA)
    {
        int m=l+r>>1;
        st[num].init(l,r);
        if(l!=r)
            init(v,gl),init(v,gr),pushUp(num);
        else
            st[num].mark(v[l]);
    }
    void update(int a,int b,int v,PARA)
    {
        pushDown(num);
        if(a<=l&&r<=b)
            st[num].mark(v);
        else
        {
            int m=l+r>>1;
            if(b<=m)
                update(a,b,v,gl);
            else if(a>m)
                update(a,b,v,gr);
            else
                update(a,b,v,gl),update(a,b,v,gr);
            pushUp(num);
        }
    }
    Node query(int a,int b,PARA)
    {
        pushDown(num);
        Node ret;
        if(a<=l&&r<=b)
            return st[num];
        int m=l+r>>1;
        if(b<=m)
            ret=query(a,b,gl);
        else if(a>m)
            ret=query(a,b,gr);
        else
            ret.merge(query(a,b,gl),query(a,b,gr));
        pushUp(num);
        return ret;
    }
} tr;
const int maxn=MAXN;
const int maxm=maxn+maxn;

struct EDGENODE
{
    int to;
    int next;
} edges[maxm];
int head[maxn],edge;
inline void init()
{
    edge=0;
    memset(head,-1,sizeof(head));
}
inline void addedge(int u,int v)
{
    edges[edge].to=v,edges[edge].next=head[u],head[u]=edge++;
    edges[edge].to=u,edges[edge].next=head[v],head[v]=edge++;
}
int que[maxn]; // 队列
bool vis[maxn]; // 访问标记
int son[maxn]; // 重儿子
int idx[maxn]; // 结点v在其路径中的编号
int dep[maxn]; // 结点v的深度
int siz[maxn]; // 以结点v为根的子树的结点个数
int belong[maxn]; // 结点v所属的路径编号
int fa[maxn]; // 结点v的父亲结点
int top[maxn]; // 编号为p的路径的顶端结点
int len[maxn]; // 路径p的长度
int sump[maxn]; // 路径p的编号
int seg[maxn]; // 结点v的父边在线段树中的位置
int wei[maxn]; // 结点v的父边的权值
int l,r,ans,cnt;
int n;
char cmd[22];

void split()
{
    memset(dep,-1,sizeof(dep));
    l=0;
    dep[ que[r=1]=1 ]=0; // 将根结点插入队列,并设深度为0
    fa[1]=-1; // 默认 1 为根结点
    while (l<r)  // 第一遍搜索求出 fa,dep,wei
    {
        int u=que[++l];
        vis[u]=false; // 顺便初始化vis
        for (int i=head[u]; i!=-1; i=edges[i].next)
        {
            int v=edges[i].to;
            if (dep[v]==-1)  // 未访问过的结点
            {
                dep[ que[++r]=v ]=dep[u]+1; // 将v插入队列并设深度为dep[u]+1
                fa[v]=u; // v的父结点为u
            }
        }
    }
    cnt=0; // 重链编号
    for (int i=n; i>0; i--)
    {
        int u=que[i],p=-1;
        siz[u]=1;
        son[u]=p;
        for (int k=head[u]; k!=-1; k=edges[k].next)
        {
            int v=edges[k].to;
            if (vis[v])  // 若v是u的子结点
            {
                siz[u]+=siz[v]; // 计数
                if (p==-1||siz[v]>siz[p])
                {
                    son[u]=v;
                    p=v; // u的重儿子是v
                }
            }
        }
        if (p==-1)  // u是叶子结点
        {
            idx[u]=len[++cnt]=1; // 一个新的路径编号为cnt,u是路径中的第一个结点
            belong[ top[cnt]=u ]=cnt; // u是顶端结点,且u属于路径cnt
        }
        else  // u不是叶子结点
        {
            idx[u]=++len[ belong[u]=belong[p] ]; // u属于重儿子所在的链,链长+1,u是路径中第len个结点
            top[ belong[u] ]=u; // u是顶端结点
        }
        vis[u]=true; // 访问标记
    }
    sump[0]=0;
    for (int i=1;i<=cnt;i++) sump[i]=sump[i-1]+len[i];
    for (int i=1;i<=n;i++){
        seg[i]=sump[ belong[i] ]-idx[i]+1;
        tr.num[ seg[i] ]=wei[i];
    }
}
int in[MAXN];

void build()
{
    int a,b;
    for(int i=1;i<=n;i++)
        scanf("%d",&wei[i]);
    for(int i=1;i<n;i++)
        scanf("%d%d",&a,&b),addedge(a,b);
    split();
    for(int i=1;i<=n;i++)
        in[seg[i]]=wei[i];
    L=1,R=n;
    tr.init(in);
}

Node q1[MAXN],q2[MAXN];
long long find(int va,int vb)
{
    int f1=top[belong[va]],f2=top[belong[vb]];
    int s1=0,s2=0;
    while (f1!=f2)
    {
        if (dep[f1]<dep[f2])
        {
            q2[s2++]=tr.query(seg[f2],seg[vb]);
            vb=fa[f2];
            f2=top[belong[vb]];
        }
        else
        {
            q1[s1]=tr.query(seg[f1],seg[va]);
            q1[s1++].swap();
            va=fa[f1];
            f1=top[belong[va]];
        }
    }
    for(int i=1;i<s1;i++)
        q1[0].merge(q1[0],q1[i]);
    for(int i=1;i<s2;i++)
        q2[0].merge(q2[i],q2[0]);
    int d=0;
    if (dep[va]>dep[vb]) swap(va,vb),d=1;
    Node ret=tr.query(seg[va],seg[vb]);
    if(d)
        ret.swap();
    if(s1)
        ret.merge(q1[0],ret);
    if(s2)
        ret.merge(ret,q2[0]);
    return ret.mx;
}

void update(int va,int vb,int v){
    int f1=top[belong[va]],f2=top[belong[vb]],tmp=-INF;
    while (f1!=f2){
        if (dep[f1]<dep[f2]){
            swap(f1,f2);
            swap(va,vb);
        }
        tr.update(seg[f1],seg[va],v);
        va=fa[f1];
        f1=top[belong[va]];
    }
    if (dep[va]>dep[vb]) swap(va,vb);
    tr.update(seg[va],seg[vb],v);
}

int main()
{
    int q,a,b,c,d;
    while(scanf("%d%d",&n,&q)!=EOF)
    {
        init();
        build();
        for(int i=0;i<q;i++)
        {
            scanf("%d%d%d%d",&a,&b,&c,&d);
            if(a==1)
                update(b,c,d);
            else
                printf("%lld\n",find(b,c));
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值