题目
求不下降序列的某个区间中出现次数最多的数。
分析
明显的线段树。
用left表示左边的连续个数,right表示右边的连续个数,w表示最长的连续个数。
如果左边的所有数都等于右边最左的数或如果右边的所有数都等于左边最右的数则:
t
r
e
e
[
k
]
.
r
i
g
h
t
=
t
r
e
e
[
k
∗
2
]
.
r
i
g
h
t
+
t
r
e
e
[
k
∗
2
+
1
]
.
l
e
f
t
tree[k].right=tree[k∗2].right+tree[k∗2+1].left
tree[k].right=tree[k∗2].right+tree[k∗2+1].left
如果左边最右边的数等于右边最左边的数则最优解在中间:
t
r
e
e
[
k
]
.
m
a
x
s
=
m
a
x
(
t
r
e
e
[
k
∗
2
]
.
m
a
x
s
,
t
r
e
e
[
k
∗
2
+
1
]
.
m
a
x
s
,
t
r
e
e
[
k
∗
2
]
.
r
i
g
h
t
+
t
r
e
e
[
k
∗
2
+
1
]
.
l
e
f
t
)
tree[k].maxs=max(tree[k∗2].maxs,tree[k∗2+1].maxs,tree[k∗2].right+tree[k∗2+1].left)
tree[k].maxs=max(tree[k∗2].maxs,tree[k∗2+1].maxs,tree[k∗2].right+tree[k∗2+1].left)
代码
#include <cstdio>
#include <cstring>
#define g (k<<1)
using namespace std;
int n,m,b[100001];
struct treen{int left,right,w;}tree[300001];
int max(int a,int b){return (a>b)?a:b;}
int min(int a,int b){return (a<b)?a:b;}
void build(int k,int l,int r){
if (l==r) tree[k].left=tree[k].right=tree[k].w=1;
else {
int mid=(l+r)>>1;
build(g,l,mid); build(1+g,mid+1,r);
if (tree[g].left==mid-l+1&&b[mid]==b[mid+1])
tree[k].left=tree[g].right+tree[1+g].left;
else tree[k].left=tree[g].left;
if (tree[1+g].right==r-mid&&b[mid]==b[mid+1])
tree[k].right=tree[g].right+tree[1+g].left;
else tree[k].right=tree[1+g].right;
if (b[mid]==b[mid+1])
tree[k].w=max(max(tree[g].w,tree[1+g].w),tree[g].right+tree[1+g].left);
else tree[k].w=max(tree[g].w,tree[1+g].w);
}
}
int find(int k,int l,int r,int x,int y){
if (l>y||r<x||r<l) return 0;
if (l>=x&&r<=y) return tree[k].w;
int mid=(l+r)>>1; int m=0,m1=0,m2=0;
if (b[mid]==b[mid+1]) m=min(mid-x+1,tree[g].right)+min(y-mid,tree[1+g].left);
if (x<=mid) m1=find(g,l,mid,x,y);
if (y>mid) m2=find(1+g,mid+1,r,x,y);
return m=max(m,max(m1,m2));
}
int main(){
while (scanf("%d",&n)&&n){
scanf("%d",&m); memset(tree,0,sizeof(tree));
for (int i=1;i<=n;i++) scanf("%d",&b[i]);
build(1,1,n); int x,y;
while (m--){
scanf("%d%d",&x,&y);
printf("%d\n",find(1,1,n,x,y));
}
}
return 0;
}