题目链接 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)));
}
}