POJ 3368 Frequent values 线段树

题意:

给你N个数,按照非减序列给出;

然后M个询问,每个询问一个区间,问在(a,b)之间最多相同数量数字的个数。

Sample Input

10 3
-1 -1 1 1 1 1 3 10 10 10
2 3
1 10
5 10
0

Sample Output

1
4
3
思路:

第一道线段树,以前只会树状数组,然后今天碰上这道题,就去学习了一下线段树。

发现其实树状数组能做的题目线段树都可以做,所以还是得好好弄懂这个。

参考了别人的解题报告,总算是写出来了。

期间WA了无数次,各种小错误,最后终于A掉了。太辛苦了

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <string>
#include <cmath>
#include <cstring>
#include <queue>
#include <set>
#include <vector>
#include <stack>
#include <map>
#include <iomanip>
#define PI acos(-1.0)
#define Max 100005
#define inf 1<<28
using namespace std;

struct kdq
{
    int l,r,max;
} tree[Max*4];//树
struct qdk
{
    int start,end;
} seg[Max];
int point[Max];
int hash[Max];
int k=0;

void build_tree(int l,int r,int u)//建树
{
    tree[u].l=l;
    tree[u].r=r;
    if(l==r)
    {
        tree[u].max=seg[l].end-seg[l].start+1;
        return ;
    }
    int mid=(l+r)/2;
    build_tree(l,mid,u<<1);//左子树
    build_tree(mid+1,r,(u<<1)+1);//右子树
    tree[u].max=max(tree[u<<1].max,tree[(u<<1)+1].max);//父节点的最大值是左子树和右子树的最大值
}

int query(int left,int right,int u)//询问
{
    if(left==tree[u].l&&right==tree[u].r)
        return tree[u].max;
    if(right<=tree[u<<1].r)//如果right小于左子树的 right,则将left,right插入左子树。
        return query(left,right,u<<1);
    if(left>=tree[(u<<1)+1].l)//如果left大于右子树的left,则将left,right插入右子树。
        return query(left,right,(u<<1)+1);
    int a=query(left,tree[u<<1].r,u<<1);
    int b=query(tree[(u<<1)+1].l,right,(u<<1)+1);
    return max(a,b);//输出最大值
}
int main()
{
    int i,j,l,n,m;
    while(scanf("%d",&n),n)
    {
        cin>>m;
        for(i=1; i<=n; i++)
            scanf("%d",&point[i]);
        k=0;
        int pre=10000000;
        for(i=1; i<=n; i++)//离散化处理
        {
            if(point[i]!=pre)
            {
                k++;
                seg[k].start=i;
                seg[k].end=i;
                pre=point[i];
            }
            else
                seg[k].end=i;//所有相同的数为一个集合。
            hash[i]=k;
        }
        int pos1,pos2;
        build_tree(1,k,1);
        int aa,bb;
        while(m--)
        {
            scanf("%d%d",&aa,&bb);
            pos1=hash[aa];
            pos2=hash[bb];
            if(hash[aa]==hash[bb])//如果在一个集合内,则输出他们的差值记为最大的出现次数。
            {
                printf("%d\n",bb-aa+1);
                continue;
            }
            int num3=0;
            int num1=seg[pos1].end-aa+1;//point[aa]的出现次数
            int num2=bb-seg[pos2].start+1;//point[bb]的出现次数
            if(pos2-pos1>1)//如果两个集合之间还有集合
                num3=query(pos1+1,pos2-1,1);//则继续计算
            printf("%d\n",max(max(num1,num2),num3));
        }
    }
    return 0;
}
继续继续



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值