UPC组队赛第四场—— H: Boring Counting (主席树+二分)

问题 H: Boring Counting

时间限制: 3 Sec 内存限制: 128 MB

题目描述
In this problem you are given a number sequence P consisting of N integer and Pi is the ith element in the sequence. Now you task is to answer a list of queries, for each query, please tell us among [L, R], how many Pi is not less than A and not greater than B( L<= i <= R). In other words, your task is to count the number of Pi (L <= i <= R, A <= Pi <= B).

输入
In the first line there is an integer T (1 < T <= 50), indicates the number of test cases.
For each case, the first line contains two numbers N and M (1 <= N, M <= 50000), the size of sequence P, the number of queries. The second line contains N numbers Pi(1 <= Pi <= 10^9), the number sequence P. Then there are M lines, each line contains four number L, R, A, B(1 <= L, R <= n, 1 <= A, B <= 10^9)

输出
For each case, at first output a line ‘Case #c:’, c is the case number start from 1. Then for each query output a line contains the answer.

样例输入 Copy
1
13 5
6 9 5 2 3 6 8 7 3 2 5 1 4
1 13 1 10
1 13 3 6
3 6 3 6
2 8 2 8
1 9 1 9
样例输出 Copy
Case #1:
13
7
3
6
9

题意:

给定一个长度为n的序列和m次询问,每次询问给出L,R,A,B,问在下标为[ L,R ] 的序列里,满足A<=X<=B的X有多少个。

思路:

如果我们知道A是第几个数,B是第几个数,就能计算出在A和B之间有多少个数。
对于求区间第K大的值,可以用主席树求;现在是知道值求该值是区间的第几个数,可以用二分来做。
需要注意判断一下A和B之间没有数的情况。

代码:

#pragma GCC optimize(3)
#pragma GCC optimize("Ofast","unroll-loops","omit-frame-pointer","inline")
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<ll,ll>PLL;
typedef pair<int,int>PII;
typedef pair<double,double>PDD;
#define I_int ll
#define x first
#define y second
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
char F[200];
inline void out(I_int x) {
    if (x == 0) return (void) (putchar('0'));
    I_int tmp = x > 0 ? x : -x;
    if (x < 0) putchar('-');
    int cnt = 0;
    while (tmp > 0) {
        F[cnt++] = tmp % 10 + '0';
        tmp /= 10;
    }
    while (cnt > 0) putchar(F[--cnt]);
    //cout<<" ";
}
ll ksm(ll a,ll b,ll p){ll res=1;while(b){if(b&1)res=res*a%p;a=a*a%p;b>>=1;}return res;}
const int inf=0x3f3f3f3f,mod=1000000007;
const ll INF = 0x3f3f3f3f3f3f3f3f;
const int maxn=1e5+7,maxm=3e5+7;
const double PI = atan(1.0)*4;
 
int n,m;
int a[maxn];
vector<int>nums;
 
struct node{
    int l,r,cnt;
}tr[maxn*20];
int root[maxn],idx;
 
int Find(int x){
    return lower_bound(nums.begin(),nums.end(),x)-nums.begin();
}
 
int build(int l,int r){
    int p=++idx;
    if(l==r) return p;
    int mid=(l+r)>>1;
    tr[p].l=build(l,mid);tr[p].r=build(mid+1,r);
    return p;
}
 
int Insert(int p,int l,int r,int x){
    int q=++idx;
    tr[q]=tr[p];
    if(l==r){
        tr[q].cnt++;
        return q;
    }
    int mid=(l+r)>>1;
    if(x<=mid) tr[q].l=Insert(tr[p].l,l,mid,x);
    else tr[q].r=Insert(tr[p].r,mid+1,r,x);
    tr[q].cnt=tr[tr[q].l].cnt+tr[tr[q].r].cnt;
    return q;
}
int qask(int q, int p, int l, int r, int k)
{
    if (l == r) return r;
    int cnt = tr[tr[q].l].cnt - tr[tr[p].l].cnt;
    int mid=(l+r)>>1;
    if (k <= cnt) return qask(tr[q].l, tr[p].l, l, mid, k);
    else return qask(tr[q].r, tr[p].r, mid + 1, r, k - cnt);
}
 
 
int main(){
    int T=read();
    for(int Case=1;Case<=T;Case++){
        printf("Case #%d:\n",Case);
        nums.clear();idx=0;
        memset(tr,0,sizeof tr);memset(root,0,sizeof root);
        n=read(),m=read();
        for(int i=1;i<=n;i++){
            a[i]=read();nums.push_back(a[i]);
        }
        sort(nums.begin(),nums.end());
        nums.erase(unique(nums.begin(), nums.end()), nums.end());
        root[0]=build(0,nums.size()-1);
        for(int i=1;i<=n;i++)
            root[i]=Insert(root[i-1],0,nums.size()-1,Find(a[i]));
        while(m--){
            int l=read(),r=read(),A=read(),B=read();
            ///int tmp=qask(root[r],root[l-1],0,nums.size()-1,k);区间第k大
            int res=0;
            bool flag=0;
            int la=1,ra=r-l+1,resa=-1;
            while(la<=ra){
                int mid=(la+ra)>>1;
                int tmp=qask(root[r],root[l-1],0,nums.size()-1,mid);
                if(nums[tmp]>=A) ra=mid-1,resa=mid;
                else la=mid+1;
            }
            if(resa==-1) flag=1;
            else res=res-resa+1;
            int lb=1,rb=r-l+1,resb=-1;
            while(lb<=rb){
                int mid=(lb+rb)>>1;
                int tmp=qask(root[r],root[l-1],0,nums.size()-1,mid);
                if(nums[tmp]<=B) lb=mid+1,resb=mid;
                else rb=mid-1;
            }
            if(resb==-1) flag=1;
            else res=res+resb;
            if(flag) res=0;
            ///cout<<resa<<" "<<resb<<endl;
            out(res);puts("");
        }
    }
    return 0;
}

参考博客

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

豆沙睡不醒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值