山东省第四届ACM-H Boring Counting

链接:点击打开链接

题意:

给你n个数,m个询问(N and M (1 <= N, M <= 50000)),询问包含四个数L,R,A,B 求区间[L,R]里面在区间[A,B]的数的个数。

思路:

首先想到的是而且的树状数组,或者线段树处理,因为数据量太大。但是二维这个维数也不能表示,因为离散化之后50000*50000是不能表示的,那怎么办呢?

划分树,我们只要二分枚举该区间的最小的第几大大于等于A,以及最小的大于B的第几大,然后他们的差值就是个数。时间复杂度为O(nlog(n)*log(n));

这里求区间第几大用到了划分树。

这个是输入的数字是可以重复的。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define M 50007
#define N 50007
using namespace std;
#define lc l,m,rt<<1
#define rc m + 1,r,rt<<1|1
struct node{
    int l,r;
    int mid(){
        return (l + r)>>1;
        }
}tt[N<<2];
int toLeft[30][N];
int val[30][N],sorted[N];
int n,q,m;
void build(int l,int r,int rt,int d)
{
    int i;
    tt[rt].l = l;
    tt[rt].r = r;
    if (l == r)
    return ;
    int m = tt[rt].mid();
    int lsame = m - l + 1;
    for (i = l; i <= r; ++i){
        if (val[d][i] < sorted[m]) lsame--;
        }
        int lpos = l;
        int rpos = m + 1;
        int same = 0;
        for (i = l; i <= r; ++i){
            if (i == l) toLeft[d][i] = 0;
            else toLeft[d][i] = toLeft[d][i - 1];
            if (val[d][i] < sorted[m]){
                toLeft[d][i]++; val[d + 1][lpos++] = val[d][i];
                }
                else if (val[d][i] > sorted[m]){
                    val[d + 1][rpos++] = val[d][i];
                    }
                    else{
                        if (same < lsame){
                            toLeft[d][i]++;
                            val[d + 1][lpos++] = val[d][i]; same++;
                            }
                            else{
                                val[d + 1][rpos++] = val[d][i];
                                }
                        }
                }
        build(lc,d + 1);
        build(rc,d + 1);
        }
int query(int L,int R,int k,int d,int rt){
if (L == R){
    return val[d][L];
    }
    int s = 0; int ss = 0;
    if (L == tt[rt].l){
        ss = 0; s = toLeft[d][R];
        }
        else{
            ss = toLeft[d][L - 1];
            s = toLeft[d][R] - toLeft[d][L - 1];
            }
        if (k <= s){
            int newl = tt[rt].l + ss;
            int newr = newl + s - 1;
            return query(newl,newr,k,d + 1,rt<<1);
            }
        else{
            int m = tt[rt].mid();
            int bb = L - tt[rt].l - ss;
            int b = R - L + 1 - s;
            int newl = m + bb + 1;
            int newr = newl + b - 1;
            return query(newl,newr,k - s,d + 1,rt<<1|1);
            }
}
int BS1(int L,int R,int l,int r,int A) {
    int ans = -1;
    while (l <= r) {
        int mid = (l + r)>>1;
        int res = query(L,R,mid,0,1);
        if (res >= A) {
            ans = mid; r = mid - 1;
            }
            else l = mid + 1;
            }
    return ans;
}
int BS2(int L,int R,int l,int r,int B)
{
    int ans = 0;
    while (l <= r) {
        int mid = (l + r)>>1;
        int res = query(L,R,mid,0,1);
        if (res > B) { ans = mid; r = mid - 1; }
        else l = mid + 1; }
        if (ans == 0)
        return r;
        else return ans - 1;
}
int main() {
    int T,i;
    int cas = 1;
    scanf("%d",&T);
    while (T--) {
        scanf("%d%d",&n,&m);
        for (i = 1; i <= n; ++i) {
            scanf("%d",&val[0][i]);
            sorted[i] = val[0][i];
            }
            sort(sorted + 1,sorted + 1 + n);
            build(1,n,1,0);
            printf("Case #%d:\n",cas++);
            int x,y,A,B;
            while (m--) {
                scanf("%d%d%d%d",&x,&y,&A,&B);
                int l = 1; int r = y - x + 1;
                int cnt1 = BS1(x,y,l,r,A);
                int cnt2 = BS2(x,y,l,r,B);
                if (cnt1 == -1) { printf("0\n");
                continue; }
                printf("%d\n",cnt2 - cnt1 + 1);
                }
                }
                return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值