Super Mario HDU - 4417 (主席树-离线处理)

Super Mario

  HDU - 4417 
 题目要求给出一个区间,求出区间【l,r】之内小于等于给定H的数的个数. 我们可以类比求区间第k大的方法去求区间小于等于H的,

有一个技巧就是先将H读入,将H和原数据一起离散化,但是建树的时候依然只建n棵树,最后每次求一下[0-r]  [0-l-1]

做差就可以了(注意题目区间是【0,n-1】)。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define N 100000

int a[N*4],b[N*4];
int sum[N*20],T[N*20];
int L[N*20],R[N*20];
int cnt=0;

void build(int &rt,int l,int r)
{
    rt=++cnt;
    sum[rt]=0;
    if(l==r)return ;
    int m=(l+r)>>1;
    build(L[rt],l,m);
    build(R[rt],m+1,r);
}

void update(int &rt,int pre,int l,int r,int x)
{
    rt=++cnt;
    L[rt]=L[pre],R[rt]=R[pre];
    sum[rt]=sum[pre]+1;
    if(l==r)return ;
    int m=(l+r)>>1;
    if(x<=m)update(L[rt],L[pre],l,m,x);
    else update(R[rt],R[pre],m+1,r,x);
}

int query(int rt,int l,int r,int k)
{
    int ans=0;
    while(l<r)
    {
        int mid=(l+r)>>1;
        if(k<mid)
        {
            rt=L[rt];
            r=mid;
        }
        else
        {
            ans+=sum[L[rt]];
            rt=R[rt];
            l=mid+1;
        }
    }
    return ans;
}

int be[N],en[N],H[N];

int main()
{
    int t;
    scanf("%d",&t);
    for(int cas=1;cas<=t;cas++)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a[i]);
            b[i]=a[i];
        }
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d%d",&be[i],&en[i],&H[i]);
            b[i+n]=H[i];
        }
        sort(b+1,b+1+n+m);
        int len=unique(b+1,b+1+n+m)-b-1;
        cnt=0;
        build(T[0],1,len);
        for(int i=1;i<=n;i++)
        {
            int pos=lower_bound(b+1,b+1+len,a[i])-b;
            update(T[i],T[i-1],1,len,pos);
        }
        printf("Case %d:\n",cas);
        for(int i=1;i<=m;i++)
        {
            int p1=lower_bound(b+1,b+1+len,H[i])-b;
            int k1=query(T[en[i]+1],1,len,p1);
            int k2=query(T[be[i]],1,len,p1);
            printf("%d\n",k1-k2);
        }
    }
    return 0;
}

参考了大牛的题解发现可以用树状数组离线处理,具体思路是这样的。

对于区间里的每一个值,我们按照从小到大排序,然后我们按照位置依次插入

同样的,对于要查询的H我们也将它从小到大排序,这样我们就能保证,当查询到较大的H时

比H小的值都已经插进树状数组里了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 100005
#define lowb(x) x&-x;
struct node
{
    int k,id;
}a[N];

struct node1
{
    int l,r,h,id;
}H[N];
int L[N],R[N];
int C[N];
int ans[N];
bool cmp(node x,node y)
{
    return x.k<y.k;
}

bool cmp1(node1 x,node1 y)
{
    return x.h<y.h;
}
void add(int pos)
{
    while(pos<=N)
    {
        C[pos]+=1;
        pos+=lowb(pos);
    }
}

int getsum(int x)
{
   int ans=0;
   while(x)
   {
       ans+=C[x];
       x-=lowb(x);
   }
   return ans;
}

void solve(int m,int n)
{
    int top=1;
    for(int i=1;i<=m;i++)
    {
        while(top<=n&&a[top].k<=H[i].h)
        {
            add(a[top].id);
            top++;
        }
//        printf("%d %d %d\n",H[i].l,H[i].r,H[i].h);
//        printf("%d %d\n",getsum(H[i].l-1),getsum(H[i].r));
        ans[H[i].id]=getsum(H[i].r)-getsum(H[i].l-1);
    }
    for(int i=1;i<=m;i++)
    {
        printf("%d\n",ans[i]);
    }
}
int main()
{
     int t;
     cin>>t;
     for(int cas=1;cas<=t;cas++)
     {
         int n,m;
         memset(C,0,sizeof(C));

         scanf("%d%d",&n,&m);
         for(int i=1;i<=n;i++)
         {
             scanf("%d",&a[i].k);
             a[i].id=i;
         }
         sort(a+1,a+1+n,cmp);
         int x,y;
         for(int i=1;i<=m;i++)
         {
             scanf("%d%d%d",&x,&y,&H[i].h);
             H[i].l=x+1,H[i].r=y+1;
             H[i].id=i;
         }
         sort(H+1,H+1+m,cmp1);
         printf("Case %d:\n",cas);
         solve(m,n);
     }
}

猜你喜欢

转载自blog.csdn.net/weixin_40894017/article/details/80877150