hdu 4417 Super Mario(划分树或树状数组)

题意:给你n个数,m个查询,对于一个查询问在[a,b]范围内小于c的数有多少个。

有两种做法:1 树状数组离线处理,将输入的n个数按大小排序,也将查询按他们查询的数的大小排序。从小到大到这n个数放进数状数组里,当小于一个查询的c的所有的数都在放进数状数组里面的时候,查询[a,b]范围内有多少个数。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <algorithm>
using namespace std;
const int N=1e5+5;
struct node
{
    int valu,ind;
    node(){}
    node(int a,int b){valu=a;ind=b;}
    bool operator<(const node &b)const
    {
        return valu<b.valu;
    }
};
struct Query
{
    int st,ed,valu,ind;
    Query(){}
    Query(int a,int b,int c,int d){st=a;ed=b;valu=c;ind=d;}
    bool operator<(const Query &b)const
    {
        return valu<b.valu;
    }
};
struct BIT
{
    int a[N];
    int lowbit(int x){return x&(-x);}
    void clear() {memset(a,0,sizeof(a));}
    void updata(int pos)
    {
        for(int i=pos;i>0;i-=lowbit(i))
            a[i]+=1;
    }
    int query(int st,int ed)
    {
        int sum=0;
        for(int i=st;i<N;i+=lowbit(i))
            sum+=a[i];
        for(int i=ed+1;i<N;i+=lowbit(i))
            sum-=a[i];
        return sum;
    }
}bit;

int res[N];
vector<node> data;
vector<Query> op;

int main()
{
    int t,t_cnt=0;
    scanf("%d",&t);
    while(t--)
    {
        op.clear(); data.clear(); bit.clear();

        int n,m,a,b,c;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&a);
            data.push_back(node(a,i));
        }
        for(int i=0;i<m;i++)
        {
            scanf("%d%d%d",&a,&b,&c);
            op.push_back(Query(a+1,b+1,c,i));
        }
        sort(data.begin(),data.end());
        sort(op.begin(),op.end());
        int ind=0;
        for(int i=0;i<(int)data.size();i++)
        {
            while(ind<(int)op.size()&&op[ind].valu<data[i].valu) 
            {
                res[op[ind].ind]=bit.query(op[ind].st,op[ind].ed);
                ind++;
            }
            bit.updata(data[i].ind);
        }
        for(int i=ind;i<(int)op.size();i++)
            res[op[i].ind]=bit.query(op[i].st,op[i].ed);
        printf("Case %d:\n",++t_cnt);
        for(int i=0;i<m;i++)
            printf("%d\n",res[i]);
    }
    return 0;
}
第二种做法是划分树。对于每个查询,二分区间里的第k大数,找出大于要查询的c的第一个数的是第k大,那么答案就是这个k-1。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define MID(a,b) (a+((b-a)>>1))
const int N=1e5+5;
struct P_Tree
{
    int n,order[N];
    int valu[20][N],num[20][N];
    void init(int len)
    {
        n=len;
        for(int i=0;i<20;i++) valu[i][0]=0,num[i][0]=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&order[i]);
            valu[0][i]=order[i];
        }
        sort(order+1,order+1+n);
        build(1,n,0);
    }
    void build(int lft,int rht,int ind)
    {
        if(lft==rht) return;

        int mid=MID(lft,rht);
        int ln=lft,rn=mid+1,same=mid-lft+1;
        for(int i=lft;i<=rht;i++)
            if(valu[ind][i]<order[mid]) same--;
        for(int i=lft;i<=rht;i++)
        {
            int flag=0;
            if((valu[ind][i]<order[mid])||(valu[ind][i]==order[mid]&&same))
            {
                flag=1;
                valu[ind+1][ln++]=valu[ind][i];
                if(valu[ind][i]==order[mid]) same--;
            }
            else valu[ind+1][rn++]=valu[ind][i];
            num[ind][i]=num[ind][i-1]+flag;
        }
        build(lft,mid,ind+1);
        build(mid+1,rht,ind+1);
    }
    int query(int st,int ed,int k,int lft,int rht,int ind)
    {
        if(k==0) return -1;
        if(ed-st+1<k) return (1<<30);
        if(lft==rht) return valu[ind][lft];

        int mid=MID(lft,rht);
        int lx=num[ind][st-1]-num[ind][lft-1];
        int ly=num[ind][ed]-num[ind][st-1];
        int rx=st-1-lft+1-lx;
        int ry=ed-st+1-ly;
        if(ly>=k) return query(lft+lx,lft+lx+ly-1,k,lft,mid,ind+1);
        else
        {
            st=mid+1+rx;
            ed=mid+1+rx+ry-1;
            return query(st,ed,k-ly,mid+1,rht,ind+1);
        }
    }
}tree;
int main()
{
    int t,t_cnt=0;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        tree.init(n);
        printf("Case %d:\n",++t_cnt);
        for(int i=0;i<m;i++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            a++; b++;
            int lft=0,rht=b-a+2;
            while(lft<rht)
            {
                int mid=MID(lft,rht);
                int ele=tree.query(a,b,mid,1,n,0);
                if(ele<=c) lft=mid+1;
                else rht=mid;
            }
            printf("%d\n",lft-1);
        }
    }
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值