区间第k大问题 权值线段树 hdu 5249

先说下权值线段树的概念吧  

权值平均树 就是指区间维护值为这个区间内点出现次数和的线段树

用这个加权线段树 解决第k大问题就很方便了 

int query(int l,int r,int rt,int k)//找第k大的数
{
    if(l==r) return l;
    int m=(l+r)/2;
    if(k<=sum[rt<<1]) return query(lson,k);//看左儿子的sum是否大于k大于的话 说明第k大的树在左儿子(利用出现的次数进行比对---建树的时候 边界是递增的)
    else return query(rson,k-sum[rt<<1]);
}
最后说一下使用权值线段树容易出现的问题 在建树的过程中 树的最大边界得包括我们需要的数据 那么问题来了 当数据很大的时候 不久爆内存了
这里得将数据离散化一下
什么是离散化呢 就是将数据映射成紧凑的数据 三个步骤
1.排序 2.去重 unqinue()函数。。 3.二分定位 用位置映射数据(lower_bound函数。。)

附上吴迎学长离散化的标准代码

#include<iostream>
#include<algorithm>
#include<cstdio>
using namespace std;
int a[100],b[100],c[100],n;
int solve()//离散化
{
    for(int i=0;i<n;i++) b[i]=a[i];
    sort(b,b+n);
    int m=unique(b,b+n)-b;//去重
    for(int i=0;i<n;i++) c[i]=lower_bound(b,b+m,a[i])-b;//二分找对应位置
    for(int i=0;i<n;i++) printf("%d ",c[i]);
}
int main()
{

    freopen("in.txt","r",stdin);
    cin>>n;
    for(int i=0;i<n;i++) cin>>a[i];
    solve();
    return 0;
}

再说说5249吧。,。 看了下吴迎学长的代码。。 感触挺深。 自己按照思路码了一遍 库函数好用

#include<cstdio>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<queue>
#define lson l,m,rt<<1 //显得代码比较精简
#define rson m+1,r,rt<<1|1
#define maxn 10005
using namespace std;
int sum[maxn<<2];
int n,a[maxn],b[maxn],c[maxn];
void Pushup(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
void build(int l,int r,int rt)
{
    if(l==r)
    {
        sum[rt]=0;
        return ;
    }
    int m=(l+r)/2;
    build(lson);
    build(rson);
    Pushup(rt);
}
void updata(int l,int r,int rt,int temp,int flag)
{
    if(l==r)
    {
        sum[rt]+=flag;
        return;
    }
    int m=(l+r)/2;
    if(temp<=m) updata(lson,temp,flag);//
    else updata(rson,temp,flag);
    Pushup(rt);
}
int query(int l,int r,int rt,int k)//找第k大的数
{
    if(l==r) return l;
    int m=(l+r)/2;
    if(k<=sum[rt<<1]) return query(lson,k);
    else return query(rson,k-sum[rt<<1]);
}
int main()
{
    int t=0;
    while(~scanf("%d",&n))//由于需要离散化的过程 我们需要离线写
    {
        char op[10];
        queue<int> que;
    //    freopen("in.txt","r",stdin);
        for(int i=1;i<=n;i++)
        {
            scanf("%s",op);
            if(op[0]=='i') scanf("%d",&a[i]);
            else if(op[0]=='o') a[i]=-1;
            else a[i]=-2;
        }
        int m=1;
        for(int i=1;i<=n;i++)
        {
            if(a[i]>=0) b[m]=a[i],c[m++]=a[i];
        }
        sort(c+1,c+m);
        int mm=unique(c+1,c+m)-(c+1);//离散的排序 去重 unique函数的参数为需要去重的范围 返回值为最后一个去重过程的函数 还有就是这个函数只对相邻的数去重
        build(1,mm,1);
        printf("Case #%d:\n",++t);
    //    cout<<mm<<endl;
    //    for(int i=1;i<=mm;i++) cout<<c[i]<<endl;
        for(int i=1;i<=n;i++)
        {
            if(a[i]>=0)
            {
               int temp=lower_bound(c+1,c+mm+1,a[i])-c;//离散坐标对应
               updata(1,mm,1,temp,1);
            //   cout<<temp<<endl;
             //  for(int i=1;i<=7;i++) cout<<"in"<<sum[i]<<endl;
               que.push(a[i]);
            }
            else if(a[i]==-1)
            {
               int ret=que.front();
               int temp=lower_bound(c+1,c+mm+1,ret)-c;// 在c数组中找到第一个不小于ret的数
               updata(1,mm,1,temp,-1);
            //for(int i=1;i<=7;i++) cout<<' '<<"out"<<sum[i]<<endl;
               que.pop();
            }
            else
            {
              // cout<<que.size()/2+1<<endl;
               printf("%d\n",c[query(1,mm,1,(que.size()/2+1))]);
            }
        }
    }
    return 0;
}
posted @ 2016-08-12 14:34  猪突猛进!!!  阅读(593)  评论(0编辑  收藏  举报