正题
链接: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;elsetree[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;elsetree[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);elsetree[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));}}
}