uestc 1425 线段树 区间合并

题目:http://acm.uestc.edu.cn/problem.php?pid=1425

题意:给你一个数列,每次回选择一个区间的数,或者把这些数都加上一个值,或者问你这些数的最长递增序列。。。

分析:这题对区间进行操作,很容易使我们想到线段树,但是,线段树要保存那些信息才能求得答案呢,成段的累加这个不是问题,只要加个延迟标记就行,最主要的是询问最长递增序列。。。

1.当然,我们肯定要保存区间的最大值,每次向上更新时,理首当然的取左右子树的最大值。。。但是如果两个区间合并出更长的序列呢?

2.保存每个区间左右边界的值,这个方便我们判断两个区间是否能合并,这样向上更新时就能知道是否会出现更优值了。。。

3.知道可能合并,那我们怎么求出现的更优值呢,当然,它会出现在中间,我们需要知道从左线段的右端可以到达的最左端 lmost,和右线段左端能到达的最右端rmost ,新的值就是 rmost-lmost+1,所以我们要保存所有线段左端点能到达的最右端,右端点能到达的最左端。。。。也就是lmost ,rmost

 

///毫无疑问:经典的LICS 线段树区间更新
/// 我们用lmax[rt] 表示 以左开端LIS,rmax[rt] 表示以右结尾的LIS,mmax[rt]表示全局的LIS
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=100002;
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
int lmax[maxn<<2],rmax[maxn<<2],mmax[maxn<<2],vall[maxn<<2],valr[maxn<<2],col[maxn<<2],sum[maxn<<2],flag[maxn<<2],n,m,t;
char s[2];
void pushup(int rt,int l,int r)
{
    int mid=(l+r)>>1;
    lmax[rt]=lmax[rt<<1];///左孩子的以左开端的LIS
    rmax[rt]=rmax[rt<<1|1];///右孩子的以右结尾的LIS
    mmax[rt]=max(mmax[rt<<1],mmax[rt<<1|1]);
    vall[rt]=vall[rt<<1],valr[rt]=valr[rt<<1|1];
    if(valr[rt<<1]<vall[rt<<1|1])
    {
        if(flag[rt<<1]) lmax[rt]+=lmax[rt<<1|1];///左孩子满,可以合并
        if(flag[rt<<1|1])rmax[rt]+=rmax[rt<<1];///右孩子满,可以合并
        mmax[rt]=max(mmax[rt],rmax[rt<<1]+lmax[rt<<1|1]);///右孩子左开端+左孩子右结尾
    }
    if(mmax[rt]==r-l+1) flag[rt]=1;
    else flag[rt]=0;
}
void pushdown(int rt,int l,int r)
{
    if(col[rt])
    {
        col[rt<<1]+=col[rt];
        col[rt<<1|1]+=col[rt];
        vall[rt<<1]+=col[rt];
        valr[rt<<1]+=col[rt];
        vall[rt<<1|1]+=col[rt];
        valr[rt<<1|1]+=col[rt];
        col[rt]=0;
    }
}
void build(int rt,int l,int r)
{
    lmax[rt]=rmax[rt]=mmax[rt]=flag[rt]=1;
    col[rt]=0;
    if(l==r)
    {
        scanf("%d",&vall[rt]);
        valr[rt]=vall[rt];
        return;
    }
    int mid=(l+r)>>1;
    build(lson);
    build(rson);
    pushup(rt,l,r);
}
void updata(int rt,int l,int r,int L,int R,int num)
{
    if(L<=l&&r<=R)
    {
        col[rt]+=num;
        vall[rt]+=num;
        valr[rt]+=num;
        return;
    }
    pushdown(rt,l,r);
    int mid=(l+r)>>1;
    if(mid>=L) updata(lson,L,R,num);
    if(mid<R) updata(rson,L,R,num);
    pushup(rt,l,r);
}
int query(int rt,int l,int r,int L,int R)
{
    if(L <= l && r <= R)
    {
        return mmax[rt];
    }
    int mid = (l+r)>>1;
    int ans = 0,ans1=0,ans2=0;
    if(mid >= L) ans1=query(lson,L,R);
    if(mid < R) ans2=query(rson,L,R);
    ans=max(ans1,ans2);
    if(valr[rt<<1]< vall[rt<<1|1])
    {
        ans = max(ans,min(mid-L+1,rmax[rt<<1])+min(R-mid,lmax[rt<<1|1]));
    }
    return ans;
}
int main()
{
    int cas=1;
    cin>>t;
    while(t--)
    {
        scanf("%d%d",&n,&m);
        int i,j,a,b,c;
        build(1,1,n);
        printf("Case #%d:\n",cas++);
        for(i=1; i<=m; i++)
        {
            scanf("%s",&s);
            scanf("%d %d",&a,&b);
            if(s[0]=='q')
            {
                printf("%d\n",query(1,1,n,a,b));
            }
            else
            {
                scanf("%d",&c);
                updata(1,1,n,a,b,c);
            }
        }
    }
    return 0;
}
/*
 1
 5 6
 0 1 2 3 3
 q 1 4
 a 1 5 1
 q 1 5
 a 5 5 1
 a 5 5 -4
 q 2 3
 q 4 4
2
10 10
7 7 3 3 5 9 9 8 1 8
Q 6 6
U 3 4
Q 0 1
Q 0 5
Q 4 7
Q 3 5
Q 0 2
Q 4 6
U 6 10
Q 0 9
*/


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值