数据结构专题 - 解题报告 - L

等差数列嘛,就是公差相等嘛。求最长一段序列等差数列差不多就是求公差相等的序列最长的长度,但是,

注意

这里有一点点不一样,我们假设有三个连续的数公差为1,但是我们会说这串等差数列长度为4。而在区间左端点开始便右三个连续数公差为1则不一样,因为这样的等差效果没办法延申到左端点左方,那不在我们需要查询的区间内。
处理方法是查询从左端点右方一位开始至右端点结束,然后ans++

然后就是简单的维护线段树的操作,维护等差数列,注意修改区间时

  1. 对区间右方的影响,二者间差值会改变,单点修改。
  2. 左端点单点修改+a
  3. p左方区间修改+k,p右方区间修改-k

pushup

然后就是线段树上区间合并的问题,算是本题最难写的部分——pushup
我定义了树上每一个节点如下元素:左起点右起点,左起数值右起数值,左起序列长度右起序列长度,最长等差序列的公差,最长的长度,整个区间长度。
在每一次pushup都用左儿子和右儿子的属性更新当前节点,起点和起点数值,区间长度啥的都好更新。但是区间最大长度的更新则需要讨论,大致分以下三种情况:

  1. 在左儿子区间内有最长序列
  2. 在右儿子区间内有最长序列
  3. 若合并后的最长序列跨了左右区间,则更新,判断方式在于左儿子的右端点和右儿子的左端点相等时则代表可以合并,合并后比较即可。

这也影响了左起序列的长度的讨论,如果左儿子的右起序列覆盖了全线段,且此时正好被合并成为了最长序列,那就不是当前节点继承左儿子左起序列长度的说法了,应当直接合并后与最长序列长度相等才是。
右起序列同理。


query

至于query的写法,我们不妨把query定义成一个结构体node的类型,与树上节点类型相同,这样也更方便合并答案,至于合并方式直接复制粘贴pushup的写法改改即可,其他的操作与普通线段树无异。


AC代码如下(已去掉丢人的自己手动调试部分):

#include<bits/stdc++.h>
#define maxn 200005
#define maxm 200005
#define FOR(a, b, c) for(int a=b; a<=c; a++)
#define hrdg 1000000007
#define inf 2147483647
#define llinf 9223372036854775807
#define ll long long
#define pi acos(-1.0)
#define ls p<<1
#define rs p<<1|1
using namespace std;

inline int read()
{
    char c=getchar();long long x=0,f=1;
    while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0'; c=getchar();}
    return x*f;
}

int n, m, ql, qr, a, k, p, type;
int num[maxn];
int tag[maxn<<2];
struct node{
    int l, r, rval, lval, rlen, llen, maxv, maxl, len;
}tr[maxn<<2];
//左起点右起点,左起数值右起数值,左起序列长度右起序列长度,最长等差的差值,最长的长度,整个区间长度
void pushup(int p){
    tr[p].l = tr[ls].l; tr[p].r = tr[rs].r; tr[p].len = tr[p].r - tr[p].l + 1;
    tr[p].llen = tr[ls].llen; tr[p].lval = tr[ls].lval;
    tr[p].rlen = tr[rs].rlen; tr[p].rval = tr[rs].rval;
    if(tr[ls].maxl > tr[rs].maxl)
    {
        tr[p].maxl = tr[ls].maxl;
        tr[p].maxv = tr[ls].maxv;
    }
    else
    {
        tr[p].maxl = tr[rs].maxl;
        tr[p].maxv = tr[rs].maxv;
    }
    //更新左右儿子的最长序列到父亲节点
    if(tr[ls].rval == tr[rs].lval)
    {
        if(tr[p].maxl < tr[ls].rlen + tr[rs].llen)
        {
            tr[p].maxl = tr[ls].rlen + tr[rs].llen;
            tr[p].maxv = tr[ls].rval;
        }//更新左右两儿子可能连起来的序列长度
        if(tr[ls].rlen == tr[ls].len)			//若左儿子的右起序列已经覆盖了整个左儿子节点的线段
            tr[p].llen = tr[p].maxl;
        if(tr[rs].llen == tr[rs].len)			//右儿子同
            tr[p].rlen = tr[p].maxl;
    }
}
void build(int p, int l, int r){
    tag[p] = 0;
    if(l == r)
    {
        tr[p].l = tr[p].r = l; tr[p].len = 1;
        tr[p].llen = 1; tr[p].rlen = 1; tr[p].maxl = 1;
        tr[p].lval = tr[p].rval = tr[p].maxv = 0;
        return;
    }
    int mid = (l+r) >> 1;
    build(ls, l, mid);
    build(rs, mid+1, r);
    pushup(p);
}
void load(int p, int l, int r, int k){
    tr[p].rval += k;
    tr[p].lval += k;
    tr[p].maxv += k;
    tag[p] += k;
}
void pushdown(int p, int l, int r){
    int mid = (l + r) >> 1;
    load(ls, l, mid, tag[p]);
    load(rs, mid+1, r, tag[p]);
    tag[p] = 0;
}
void update(int nl, int nr, int l, int r, int p, int k){
    if(nl > nr) return;
    if(nl <= l && nr >= r)
    {
        load(p, l, r, k);
        return;
    }
    pushdown(p, l, r);
    int mid = (l + r) >> 1;
    if(nl <= mid)
        update(nl, nr, l, mid, ls, k);
    if(nr >= mid+1)
        update(nl, nr, mid+1, r, rs, k);
    pushup(p);
}
node query(int nl, int nr, int l, int r, int p){			//直接将query定义成结构体node类型,返回值可以合并
    node ret, lll, rrr;
    if(nl > nr) return ret;
    if(nl <= l && nr >= r)
        return tr[p];
    pushdown(p, l, r);
    int mid = (l + r) >> 1;
    if(nl <= mid)
        lll = query(nl, nr, l, mid, ls);
    if(nr >= mid + 1)
        rrr = query(nl, nr, mid+1, r, rs);
    if(nl <= mid && nr < mid + 1)
        return lll;
    if(nl > mid && nr >= mid + 1)
        return rrr;
        
    //接下来几行是合并lll和rrr的操作,是赋值粘贴pushup过来的
    ret.l = lll.l; ret.r = rrr.r; ret.len = ret.r - ret.l + 1;
    ret.llen = lll.llen; ret.lval = lll.lval;
    ret.rlen = rrr.rlen; ret.rval = rrr.rval;
    if(lll.maxl > rrr.maxl)
    {
        ret.maxl = lll.maxl;
        ret.maxv = lll.maxv;
    }
    else
    {
        ret.maxl = rrr.maxl;
        ret.maxv = rrr.maxv;
    }
    if(lll.rval == rrr.lval)
    {
        if(ret.maxl < lll.rlen + rrr.llen)
        {
            ret.maxl = lll.rlen + rrr.llen;
            ret.maxv = lll.rval;
        }
        if(lll.rlen == lll.len)
            ret.llen = ret.maxl;
        if(rrr.llen == rrr.len)
            ret.rlen = ret.maxl;
    }
    
    return ret;
}

int main()
{
    n = read();
    m = read();
    build(1, 1, n);
    while(m--)
    {
        type = read();
        if(type == 0)
        {
            ql=read(); qr=read(); a=read(); k=read(); p=read();
            update(ql, ql, 1, n, 1, a);
            update(ql+1, p, 1, n, 1, k);
            update(p+1, qr, 1, n, 1, -k);
            if(qr != n)
                update(qr+1, qr+1, 1, n, 1, -a-k*(2*p-ql-qr));
        }
        else
        {
            ql = read();
            qr = read();
            int ans = query(ql+1, qr, 1, n, 1).maxl;		//从左端点右方开始查询,最后再加一
            ans++;
            printf("%d\n", ans);
        }
    }
    return 0;
}

/*
5 3
0 1 5 2 2 2
0 3 5 4 2 4
1 1 5
*/

/*
6 2
0 1 6 5 1 4
1 1 2
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值