uva11235 frequent values (区间最大出现频数)

题目大意:长度为n的非递减序列(ai<=aj,i<j),m次询问,询问[l,r]间出现次数最多的数所对应的次数(n,m<=100000)

思路分析:一开始用莫队+set的算法,但复杂度为m*sqrt(n)*log(n),果断给T掉了。。。。由于序列为非递减的,相同

                  的元素会连续出现,即每次询问为最长相同元素子序列的长度。可用线段树维护最长连续序列的长度,每

                  次更新时进行区间合并

代码

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
using namespace std;
int A[100010];
int ll[500010],rr[500010],sum[500010];  //ll,rr分别记录每个结点对应区间左右端最长连续序列的长度,sum记录该区间最长连续序列长度

void pushup(int root,int l,int r){
     int mid=(l+r)/2,a;
     sum[root]=max(sum[root*2],sum[root*2+1]);
     ll[root]=ll[root*2];
     rr[root]=rr[root*2+1];
     if(A[mid]==A[mid+1]){      //横跨左右子区间时的情况
          if(ll[root*2]==mid+1-l) ll[root]+=ll[root*2+1];
          if(rr[root*2+1]==r-mid) rr[root]+=rr[root*2];
          sum[root]=max(sum[root],rr[root*2]+ll[root*2+1]);
     }
}
void update(int root,int l,int r,int k){
    if(l==r){
        sum[root]=ll[root]=rr[root]=1;
        return;
    }
    int mid=(l+r)/2;
    if(k<=mid) update(root*2,l,mid,k);
    else update(root*2+1,mid+1,r,k);
    pushup(root,l,r);
}
int q(int root,int l,int r,int a,int b){
    int mid=(l+r)/2;
    if(l==a&&r==b) return sum[root];
    if(b<=mid) return q(root*2,l,mid,a,b);
    else if(a>mid) return q(root*2+1,mid+1,r,a,b);
    else{
        int x,ans=max(q(root*2,l,mid,a,mid),q(root*2+1,mid+1,r,mid+1,b));
        if(A[mid]==A[mid+1]){    //横跨时的情况
             x=min(rr[root*2],mid-a+1)+min(ll[root*2+1],b-mid);
             ans=max(ans,x);
        }
        return ans;
    }
}

int main(){
    int n,m,i,a,b;
    while(scanf("%d",&n)!=EOF){
        if(n==0) break;
        scanf("%d",&m);
        memset(ll,0,sizeof(ll));
        memset(rr,0,sizeof(rr));
        memset(sum,0,sizeof(sum));
        for(i=1;i<=n;i++){
            scanf("%d",&A[i]);
            update(1,1,n,i);
        }
        while(m--){
            scanf("%d%d",&a,&b);
            printf("%d\n",q(1,1,n,a,b));
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/guogai13/article/details/81880520