P3368-Frequent values【线段树】

正题

链接:http://poj.org/problem?id=3368


大意

给出一个不下降序列,求一个区域内最多相同的数的出现次数。


解题思路

线段树
用left表示左边的连续个数,right表示右边的连续个数,maxs表示最长的连续个数,然后比较。

如果左边的所有数都等于右边最左的数则:
tree[k].left=tree[k2].right+tree[k2+1].lefttree[k].left=tree[k∗2].right+tree[k∗2+1].left

如果右边的所有数都等于左边最右的数则:
tree[k].right=tree[k2].right+tree[k2+1].lefttree[k].right=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)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));
      }
    }
}

转载于:https://www.cnblogs.com/sslwyc/p/9028696.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值