正题
链接:http://poj.org/problem?id=3368
大意
给出一个不下降序列,求一个区域内最多相同的数的出现次数。
解题思路
线段树
用left表示左边的连续个数,right表示右边的连续个数,maxs表示最长的连续个数,然后比较。
如果左边的所有数都等于右边最左的数则:
tree[k].left=tree[k∗2].right+tree[k∗2+1].lefttree[k].left=tree[k∗2].right+tree[k∗2+1].left
如果右边的所有数都等于左边最右的数则:
tree[k].right=tree[k∗2].right+tree[k∗2+1].lefttree[k].right=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)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<iostream>
using namespace std;
struct treenode{
int l,r,maxs,left,right;
}tree[400001];
int n,m,x,y,num[100001];
char c;
void build(int k,int a,int b)//建树
{
tree[k].l=a;tree[k].r=b;
if (a==b) {
tree[k].left=1;
tree[k].right=1;
tree[k].maxs=1;
return;
}
int wz=(a+b)/2;
build(k*2,a,wz);
build(k*2+1,wz+1,b);
if (tree[k*2].left==wz-a+1&&num[wz]==num[wz+1])
tree[k].left=tree[k*2].right+tree[k*2+1].left;
else
tree[k].left=tree[k*2].left;
if (tree[k*2+1].right==b-wz&&num[wz]==num[wz+1])
tree[k].right=tree[k*2].right+tree[k*2+1].left;
else
tree[k].right=tree[k*2+1].right;
if (num[wz]==num[wz+1])
tree[k].maxs=max(max(tree[k*2].maxs,tree[k*2+1].maxs),tree[k*2].right+tree[k*2+1].left);
else
tree[k].maxs=max(tree[k*2].maxs,tree[k*2+1].maxs);
//统计
}
int find(int k,int a,int b)
{
if (tree[k].l>b||tree[k].r<a||tree[k].r<tree[k].l) return 0;
if (tree[k].r<=b && tree[k].l>=a)
return tree[k].maxs;
int wz=(tree[k].r+tree[k].l)/2,m=1,m1=1,m2=1;
if (num[wz]==num[wz+1])
m=min(wz-a+1,tree[k*2].right)
+min(b-wz,tree[k*2+1].left);//中间最优解
if (a<=wz)
m1=find(k*2,a,b);//左边最优解
if (b>wz)
m2=find(k*2+1,a,b);//右边最优解
m=max(m,max(m1,m2));//全部的最优解
return m;
}
int main()
{
while(scanf("%d",&n))
{
if (n==0) break;
scanf("%d",&m);
for (int i=1;i<=n;i++)
scanf("%d",&num[i]);
memset(tree,0,sizeof(tree));
build(1,1,n);
for (int i=1;i<=m;i++)
{
scanf("%d%d",&x,&y);
printf("%d\n",find(1,x,y));
}
}
}