牛客小白月赛9 - E.换个角度思考 - (树状数组离线操作 or 主席树)

版权声明:本文为博主原创文章,转载请注明出处。 https://blog.csdn.net/sdau20163942/article/details/84201024

题目链接https://ac.nowcoder.com/acm/contest/275/E

题意:给定一个序列a[1,n],有多次询问,每次查询给出 (l,r,x),表示查询区间a[l,r]里小于等于k的元素的个数。

解析:如果题目是强制在线操作,那这就是主席树的模板题(我只会套模板)。但此题不强制在在线,那么可以用树状数组离线操作(过程见代码),解法算是比较简单,但是我一个不经常用bit的菜逼也是没想到。

树状数组代码(123ms)

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+5;

int n,m,bit[MAXN],ans[MAXN];
struct Num{
    int x,id;
    bool operator<(const Num& obj)const
    {
        return x<obj.x;
    }
}a[MAXN];
struct Query{
    int l,r,k,id;
    bool operator<(const Query& obj)const
    {
        return k<obj.k;
    }
}q[MAXN];

inline int lowbit(int i){return i&(-i);}
void add(int i)
{
    while(i<=n){
        bit[i]++;i+=lowbit(i);
    }
}
int sum(int i)
{
    int res=0;
    while(i>0){
        res+=bit[i];i-=lowbit(i);
    }
    return res;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i].x);
        a[i].id=i;
    }
    sort(a+1,a+1+n);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].k);
        q[i].id=i;
    }
    sort(q+1,q+1+m);

    int it=1;
    for(int i=1;i<=m;i++)
    {
        while(a[it].x<=q[i].k&&it<=n){
            add(a[it].id);it++;
        }
        ans[q[i].id]=sum(q[i].r)-sum(q[i].l-1);
    }
    for(int i=1;i<=m;i++)
        printf("%d\n",ans[i]);
    return 0;
}

代码模板来自:https://blog.csdn.net/qq_37685156/article/details/80364092

主席树模板代码(255ms)

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
 
vector<int> v;
int n,m,a[maxn],rt[maxn],tot;
struct node
{
    int l,r,sum;
}T[maxn*20];
int getid(int x)
{
    return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
void build(int &o,int l,int r)
{
    o=++tot;
    T[o].sum=0;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(T[o].l,l,mid);
    build(T[o].r,mid+1,r);
}
void update(int l,int r,int &now,int last,int k)
{
    T[++tot]=T[last];
    now=tot;
    T[now].sum++;
    if(l==r) return;
    int mid=(l+r)>>1;
    if(k<=mid) update(l,mid,T[now].l,T[last].l,k);
    else update(mid+1,r,T[now].r,T[last].r,k);
}
int query(int l,int r,int x,int y,int k)
{
    if(l==r) return T[y].sum-T[x].sum;
    int mid=(l+r)>>1;
    if(k<=mid) return query(l,mid,T[x].l,T[y].l,k);
    else
    {
        int ret=0;
        ret+=T[T[y].l].sum-T[T[x].l].sum;
        ret+=query(mid+1,r,T[x].r,T[y].r,k);
        return ret;
    }
}
int main()
{
    v.clear();
    tot=0;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        v.push_back(a[i]);
    }
    sort(v.begin(),v.end());
    v.erase(unique(v.begin(),v.end()),v.end());
    build(rt[0],1,n);
    for(int i=1;i<=n;i++)
        update(1,n,rt[i],rt[i-1],getid(a[i]));
 
    for(int i=1;i<=m;i++)
    {
        int l,r,h;
        scanf("%d%d%d",&l,&r,&h);
        //l++,r++;//本题下标从1开始
        int k=upper_bound(v.begin(),v.end(),h)-v.begin();//v中有k个元素小于等于h
        //upper的返回值是指向键值<=key的最后一个元素的后一个元素。
        if(!k)
            printf("0\n");
        else
            printf("%d\n",query(1,n,rt[l-1],rt[r],k));
    }
    return 0;
}

 

猜你喜欢

转载自blog.csdn.net/sdau20163942/article/details/84201024