#线段树#POJ 3368 Frequent values

题目

求不下降序列的某个区间中出现次数最多的数。


分析

明显的线段树。
用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[k2].right+tree[k2+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[k2].maxs,tree[k2+1].maxs,tree[k2].right+tree[k2+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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值