[AHOI2013]作业

传送门

Description

给定了一个长度为\(n\)的数列和若干个询问,每个询问是关于数列的区间表示数列的第\(l\)个数到第\(r\)个数),首先你要统计该区间内大于等于\(a\),小于等于\(b\)的数的个数,其次是所有大于等于\(a\),小于等于\(b\)的,且在该区间中出现过的数值的个数。

\(n,m \leq 10^5\)

Solution

非常简单的莫队题目

追求更优的复杂度,可以采用\(cdq\)分治:

我们发现其实原题可以简化成一个三维数点

  • \(L \leq i\leq R\)
  • \(A\leq a[i] \leq B\)
  • \(pre[i]\leq L-1\)

于是把值离散一下,直接上\(cdq\)就行了


Code 

#include<bits/stdc++.h>
#define ll long long
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)>(b)?(b):(a))
#define reg register
inline int read()
{
    int 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<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
const int MN=1e5+5;
struct Node
{
    int x,y,z,sign,id;
    bool operator < (const Node&_)const
    {
        if(z^_.z) return z<_.z;
        return id<_.id;
    }
}a[MN*5],b[MN*5];
int ans1[MN],ans2[MN],tot;
int t[MN];
void C(int x,int val){for(;x<=MN;x+=(x&-x))t[x]+=val;}
int G(int x){int r=0;for(;x;x-=(x&-x))r+=t[x];return r;}
void cdq(int l,int r)
{
    if(l==r) return;
    reg int i,j,p=l,mid=(l+r)>>1;
    cdq(l,mid);cdq(mid+1,r);
    for(i=l,j=mid+1;i<=mid||j<=r;)
        if(j>r||(i<=mid&&(a[i].y<a[j].y||(a[i].y==a[j].y&&a[i].id<a[j].id))))
        {
            if(!a[i].id) C(a[i].x,1);b[p++]=a[i++];
        }
        else
        {
            if(a[j].id) ans2[a[j].id]+=a[j].sign*G(a[j].x);
            b[p++]=a[j++];
        }
    for(i=l;i<=mid;++i) if(!a[i].id) C(a[i].x,-1);
    for(i=l;i<=r;++i) a[i]=b[i];
}
int main()
{
    reg int i,j,N,M,num[MN],tt[MN],pre[MN],TT,_a,_b,_l,_r;
    N=read();M=read();
    for(i=1;i<=N;++i) num[i]=tt[i]=read();
    std::sort(tt+1,tt+N+1);TT=std::unique(tt+1,tt+N+1)-tt-1;
    memset(pre,0,sizeof pre);
    for(i=1;i<=N;++i)
        a[++tot]=(Node){i,j=std::lower_bound(tt+1,tt+TT+1,num[i])-tt,pre[j],0,0},pre[j]=i;
    for(i=1;i<=M;++i)
        _l=read(),_r=read(),
        _a=std::lower_bound(tt+1,tt+TT+1,read())-tt,
        _b=std::upper_bound(tt+1,tt+TT+1,read())-tt-1,
        a[++tot]=(Node){_r,_b,_l-1,1,i},
        a[++tot]=(Node){_r,_a-1,_l-1,-1,i},
        a[++tot]=(Node){_l-1,_b,_l-1,-1,i},
        a[++tot]=(Node){_l-1,_a-1,_l-1,1,i};
    std::sort(a+1,a+tot+1);cdq(1,tot);
    for(i=1;i<=tot;++i)
        if(a[i].id) ans1[a[i].id]+=a[i].sign*G(a[i].x);
        else C(a[i].x,1);
    for(i=1;i<=M;++i) printf("%d %d\n",ans1[i],ans2[i]);
    return 0;
}



Blog来自PaperCloud,未经允许,请勿转载,TKS!

转载于:https://www.cnblogs.com/PaperCloud/p/10658672.html

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值