HDU 4417 Super Mario(12年杭州 离线线段树||在线划分树)

转载请注明出处,谢谢http://blog.csdn.net/acm_cxlove/article/details/7854526       by---cxlove 

题目:给出一些数,查询区间内比H小的数有多少个

http://acm.hdu.edu.cn/showproblem.php?pid=4417 

比赛的时候SB了,其实这题还是随便搞搞的。

哭~~~TLE了半天,调整姿势用离线线段树才过。

将所有的询问离线读入之后,按H从小到大排序。然后对于所有的结点也按从小到大排序,然后根据查询的H,将比H小的点加入到线段树,然后就是一个区间和。

#include<iostream>
#include<cstdio>
#include<map>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#include<set>
#include<string>
#include<queue>
#define inf 1<<28
#define M 6000005
#define N 100005
#define maxn 300005
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))
#define pb(a) push_back(a)
#define mem(a,b) memset(a,b,sizeof(a))
#define LL long long
#define MOD 1000000007
#define lson step<<1
#define rson step<<1|1
using namespace std;
struct Tree{
    int left,right,cnt;
}L[N*4];
struct Q{
    int l,r,h,id;
    bool operator <(const Q q1)const{
        return h<q1.h;
    }
}que[N];
struct Node{
    int pos,val;
    bool operator<(const Node n1)const{
        return val<n1.val;
    }
}nod[N];
void Bulid(int step,int l,int r){
    L[step].left=l;
    L[step].right=r;
    L[step].cnt=0;
    if(l==r) return;
    int m=(l+r)/2;
    Bulid(lson,l,m);
    Bulid(rson,m+1,r);
}
void Update(int step,int pos){
    L[step].cnt++;
    if(L[step].left==L[step].right) return;
    int m=(L[step].left+L[step].right)/2;
    if(pos<=m) Update(lson,pos);
    else Update(rson,pos);
}
int Query(int step,int l,int r){
    if(L[step].left==l&&L[step].right==r)
         return L[step].cnt;
    int m=(L[step].left+L[step].right)/2;
  //  printf("%d %d %d %d\n",L[step].left,L[step].right,l,r);
    if(r<=m) return Query(lson,l,r);
    else if(l>m) return Query(rson,l,r);
    else return Query(lson,l,m)+Query(rson,m+1,r);
}
int ans[N];
int main(){
    int t,cas=0,n,q;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&q);
        for(int i=0;i<n;i++){
            scanf("%d",&nod[i].val);
            nod[i].pos=i+1;
        }
        for(int i=0;i<q;i++){
            que[i].id=i;
            scanf("%d%d%d",&que[i].l,&que[i].r,&que[i].h);
        }
        sort(nod,nod+n);
        sort(que,que+q);
        Bulid(1,1,n);
        printf("Case %d:\n",++cas);
        int k=0;
        for(int i=0;i<q;i++){
            while(k<n&&nod[k].val<=que[i].h){
                Update(1,nod[k].pos);
                k++;
            }
            ans[que[i].id]=Query(1,que[i].l+1,que[i].r+1);
        }
        for(int i=0;i<q;i++) printf("%d\n",ans[i]);
    }
    return 0;
}


其实还可以划分树做,哎

二分答案,然后判断区间的第K大数和H的关系

前者略快一点

#include<iostream>
#include<cstdio>
#include<map>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#include<set>
#include<string>
#include<queue>
#define inf 1<<28
#define M 6000005
#define N 100005
#define maxn 300005
#define Min(a,b) ((a)<(b)?(a):(b))
#define Max(a,b) ((a)>(b)?(a):(b))
#define pb(a) push_back(a)
#define mem(a,b) memset(a,b,sizeof(a))
#define LL long long
#define MOD 1000000007
#define lson step<<1
#define rson step<<1|1
using namespace std;
struct Node{
	int left,right;
	int sum;
}L[N*4];
int sa[N],num[20][N],cnt[20][N];//sa中是排序后的,num记录每一层的排序结果,cnt[deep][i]表示第deep层,前i个数中有多少个进入左子树
void Bulid(int step,int l,int r,int deep){
    L[step].left=l;
    L[step].right=r;
    if(l==r)
        return;
    int mid=(l+r)>>1;
    int mid_val=sa[mid],lsum=mid-l+1;;
    for(int i=l;i<=r;i++)
        if(num[deep][i]<mid_val)
            lsum--;    //lsum表示左子树中还需要多少个中值
    int L=l,R=mid+1;
    for(int i=l;i<=r;i++){
        if(i==l)
            cnt[deep][i]=0;
        else
            cnt[deep][i]=cnt[deep][i-1];
        if(num[deep][i]<mid_val||(num[deep][i]==mid_val&&lsum>0)){  //左子树
            num[deep+1][L++]=num[deep][i];
            cnt[deep][i]++;
            if(num[deep][i]==mid_val)
                lsum--;
        }
        else
            num[deep+1][R++]=num[deep][i];
    }
    Bulid(2*step,l,mid,deep+1);
    Bulid(2*step+1,mid+1,r,deep+1);
}
int Query(int step,int l,int r,int k,int deep){
    if(l==r)
        return num[deep][l];
    int s1,s2;   //s1为[L[step].left,l-1]中分到左子树的个数
    if(L[step].left==l)
        s1=0;
    else
        s1=cnt[deep][l-1];
    s2=cnt[deep][r]-s1;   //s2为[l,r]中分到左子树的个数
    int m=(L[step].left+L[step].right)/2;
    if(k<=s2)   //左子树的数量大于k,递归左子树
        return Query(lson,L[step].left+s1,L[step].left+s1+s2-1,k,deep+1);
    int b1=l-1-L[step].left+1-s1;  //b1为[L[step].left,l-1]中分到右子树的个数
    int b2=r-l+1-s2;   //b2为[l,r]中分到右子树的个数
    return Query(rson,m+1+b1,m+1+b1+b2-1,k-s2,deep+1);
}
int slove(int l,int r,int h){
    int ans=0,low=1,high=r-l+1,mid;
    while(low<=high){
        mid=(low+high)/2;
        int tmp=Query(1,l,r,mid,0);
        if(tmp<=h){ans=mid;low=mid+1;}
        else high=mid-1;
    }
    return ans;
}
int main(){
    int n,q,t,cas=0;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&q);
        for(int i=1;i<=n;i++){
            scanf("%d",&sa[i]);
            num[0][i]=sa[i];
        }
        sort(sa+1,sa+1+n);
        Bulid(1,1,n,0);
        printf("Case %d:\n",++cas);
        while(q--){
            int l,r,h;
            scanf("%d%d%d",&l,&r,&h);
            l++;r++;
            printf("%d\n",slove(l,r,h));
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值