等差子数列->线段树维护

题目链接 https://cn.vjudge.net/problem/HihoCoder-1710 or https://hihocoder.com/login

等差子数列

题目:

给定N个整数A1, A2, … AN,小Hi会询问你M个问题。

对于每个问题小Hi给出两个整数L和R(L ≤ R),请你找出[AL, AL+1, AL+2, … AR]中最长的等差连续子数列,并输出其长度。

例如[2, 3, 5, 7, 9]中最长的等差连续子数列是[3, 5, 7, 9]长度为4。

Input

第一行包含两个整数N和M。

第二行包含N个整数A1, A2, … AN。

以下M行每行包含两个整数L和R,代表一次询问。

对于30%的数据,1 ≤ N, M ≤ 1000

对于100%的数据,1 ≤ N, M ≤ 100000 0 ≤ Ai ≤ 10000000

Output

依次对于每个询问输出一个整数,代表答案。

Sample Input
6 2
1 2 3 5 7 9
2 6
1 4
Sample Output
4
3

题意:给你n个数,m个查询,每次查询一个区间[l, r] ,让你输出在这个区间内能形成连续的等差数列最大的长度是多少。

思路 and 当时心态历程 : 利用线段树维护, 维护指记录每两个小分支区间的两端数据,和序列总长,两端的等差序列长和等差,和总体最大等差长度,维护方法,因为不用更新节点,所以只需要建树和查找就行了,建树时的维护
1.如果两边的长度为1差值就直接时两边之差,其他长度为2
2.如果某一边长度为1,就要考虑这个与另一边的对应值维护
3.其他的按照判断它们之间的差值能否与左或右连成序列,或者中间整块连成序列,最后判断所以的最大长度赋值就行。

查询维护:按类似第三的判断看看两边是否可以连接,然后判断最大值即可。

心态:这题寒假自己曾也直接实现过,做过的题呀,比赛刚开始就思路满满,维护几乎从头写到尾,可还是没写完,气死了~。不过我的维护确实长《呵呵》
AC代码:

#include <cstdio>
#include <algorithm>
#define mid ((l+r)>>1)
#define lc (d<<1)
#define rc (d<<1|1)
const int NN = 500000;
using namespace std;
int va[NN];

struct tree
{
    int lv, rv, len, llc, rrc, ll, rl, mxl;
///lv : 左端点值, rv :右端点值, len:总长,llc左等差, rrc是右等差, ll左等差序列长,rl右等差序列长, mxl总的最大等差序列长
}tr[NN];

void push(int d)///维护
{
    tr[d].lv = tr[lc].lv, tr[d].rv = tr[rc].rv;
    tr[d].len = tr[lc].len + tr[rc].len;
    if (tr[lc].len == 1 && tr[rc].len == 1)///如果两边的长度为1差值就直接时两边之差,其他长度为2
    {
        tr[d].len = tr[d].ll = tr[d].rl = tr[d].mxl = 2;
        tr[d].rrc = tr[d].llc = tr[rc].lv - tr[lc].rv;
    }
    else if (tr[lc].len == 1)///如果某一边长度为1,就要考虑这个与另一边的对应值维护
    {
        tr[d].rrc = tr[rc].rrc;
        tr[d].llc = tr[rc].lv - tr[lc].rv;
        if (tr[d].llc == tr[rc].llc)
        {
            tr[d].ll = tr[rc].ll+1;
            if (tr[rc].rl == tr[rc].len)
                tr[d].rl = tr[d].ll;
            else tr[d].rl = tr[rc].rl;
        }
        else
        {
            tr[d].ll = 2;
            tr[d].rl = tr[rc].rl;
        }
        tr[d].mxl =  max(tr[d].rl, tr[rc].mxl);
    }
    else if (tr[rc].len == 1)
    {
        tr[d].llc = tr[lc].llc;
        tr[d].rrc = tr[rc].lv - tr[lc].rv;
        if (tr[d].rrc == tr[lc].rrc)
        {
            tr[d].rl = tr[lc].rl+1;
            if (tr[lc].ll == tr[lc].len)
                tr[d].ll = tr[d].rl;
            else tr[d].ll = tr[lc].ll;
        }
        else
        {
            tr[d].rl = 2;
            tr[d].ll = tr[lc].ll;
        }
        tr[d].mxl =  max(tr[d].ll, tr[lc].mxl);
    }
    else///其他的按照判断它们之间的差值能否与左或右连成序列,或者中间整块连成序列,最后判断所以的最大长度赋值就行。
    {
        int len = 0, cc = tr[rc].lv - tr[lc].rv;
        tr[d].rrc = tr[rc].rrc;
        tr[d].llc = tr[lc].llc;
        if (cc == tr[rc].llc && cc == tr[lc].rrc)
        {
            len = tr[lc].rl + tr[rc].ll;
            if (tr[lc].ll == tr[lc].len)
                tr[d].ll = len;
            else tr[d].ll = tr[lc].ll;
            if (tr[rc].rl == tr[rc].len)
                tr[d].rl = len;
            else tr[d].rl = tr[rc].rl;
        }
        else if (cc == tr[lc].rrc)
        {
            len = tr[lc].rl + 1;
            tr[d].rl = tr[rc].rl;
            if (tr[lc].ll == tr[lc].len)
                tr[d].ll = len;
            else tr[d].ll = tr[lc].ll;
        }
        else if (cc == tr[rc].llc)
        {
            len = tr[rc].ll + 1;
            tr[d].ll = tr[lc].ll;
            if (tr[rc].rl == tr[rc].len)
                tr[d].rl = len;
            else tr[d].rl = tr[rc].rl;
        }
        else
        {
            len = 2;
            tr[d].ll = tr[lc].ll;
            tr[d].rl = tr[rc].rl;
        }
        tr[d].mxl = max(max(tr[rc].mxl, tr[lc].mxl), len);
    }
    return;
}

void build(int d, int l, int r)
{
    if (l == r)
    {
        tr[d].lv = tr[d].rv = va[l];
        tr[d].len = tr[d].ll = tr[d].rl = 1;
        tr[d].llc = tr[d].rrc = 0;
        tr[d].mxl = 1;
        return;
    }
    build(lc, l, mid);
    build(rc, mid+1, r);
    push(d);
    //printf("%d %d %d %d %d\n", l, r, tr[d].mxl, tr[d].llc, tr[d].rrc);
}
int query(int d, int l, int r, int L, int R)
{
    if (l == L && r == R)
        return tr[d].mxl;
    if (R <= mid)
        return query(lc, l, mid, L, R);
    else if (L > mid)
        return query(rc, mid+1, r, L, R);
    else
    {
        int len = 0, cc = tr[rc].lv - tr[lc].rv;
        int ll = min((mid - L + 1), tr[lc].rl), rr = min((R - mid), tr[rc].ll);
        if (tr[lc].rrc == tr[rc].llc && tr[lc].rrc == cc)///小维护
            len = ll + rr;
        else if (tr[lc].rrc == cc)
            len =  ll+1;
        else if (tr[rc].llc == cc)
            len = rr+1;
        return max(max(query(lc, l, mid, L, mid), query(rc, mid+1, r, mid+1, R)), len);
    }
}
int main()
{
    int N, M;
    scanf("%d%d", &N, &M);
    for (int i = 1; i <= N; ++i)
        scanf("%d", va+i);
    build(1, 1, N);
    int u, v;
    for (int i = 1; i <= M; ++i)
    {
        scanf("%d%d", &u, &v);
        printf("%d\n", query(1, 1, N, min(u, v), max(u, v)));
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值