P4396 [AHOI2013]作业 莫队 + 树状数组

P4396

题意:给定一个序列,m个询问,每次询问给定一个区间 [ l , r ] 和两个数 a b ,求这个区间中有多少数在 [ a , b ] 区间之间和有多少数出现在 [ a , b ] 之间。
文字可能表述不好,下面给一组样例:
序列 1 2 2
询问 1 3 1 3 ( l , r , a , b )
答案 3 2

最近学莫队肯定是用莫队来搞了,显然 [ a , b ] 可以转换成前缀来搞,用两个树状数组来维护就行了,一个直接维护前缀个数,一个维护当前数是否出现即可。
时间复杂度: O ( N N l o g N ) O(N\sqrt{N}logN) O(NN logN)

正解应该是套上值域分块(奈何我目前不会啊。

#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define Mid (tr[u].l+tr[u].r>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define pb push_back
#define mk make_pair
#define lowbit(x) (x)&(-x)
using namespace std;

typedef long long LL;
typedef pair<int,int> PII;

const int N=1000010,mod=1e9+7,INF=0x3f3f3f3f;
const double eps=1e-6;


int n,m;
int ans1[N],ans2[N],id[N],tr1[N],tr2[N],a[N],cnt[N];
struct Node
{
    int l,r,a,b;
    int id;
}q[N];

bool cmp(Node a,Node b)
{
    return id[a.l]^id[b.l]? a.l<b.l : ((id[a.l]&1)? a.r<b.r : a.r>b.r);
}

void ad1(int x,int y)
{
    for(int i=x;i<=n;i+=lowbit(i)) tr1[i]+=y;
}

void ad2(int x,int y)
{
    for(int i=x;i<=n;i+=lowbit(i)) tr2[i]+=y;
}

int sum1(int x)
{
    int ans=0;
    for(int i=x;i;i-=lowbit(i)) ans+=tr1[i];
    return ans;
}

int sum2(int x)
{
    int ans=0;
    for(int i=x;i;i-=lowbit(i)) ans+=tr2[i];
    return ans;
}

void add(int x)
{
    x=a[x]; ad1(x,1);
    if(!cnt[x]) ad2(x,1);
    cnt[x]++;
}

void del(int x)
{
    x=a[x]; ad1(x,-1);
    cnt[x]--;
    if(!cnt[x]) ad2(x,-1);
}

int main()
{
//	ios::sync_with_stdio(false);
//	cin.tie(0);

    scanf("%d%d",&n,&m);
    int se=sqrt(n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        id[i]=(i-1)/se+1;
    }
    for(int i=1;i<=m;i++) scanf("%d%d%d%d",&q[i].l,&q[i].r,&q[i].a,&q[i].b),q[i].id=i;
    sort(q+1,q+1+m,cmp);
    int l=1,r=0;
    for(int i=1;i<=m;i++)
    {
        int lx=q[i].l,rx=q[i].r;
        int a=q[i].a,b=q[i].b;
        while(l<lx) del(l++);
        while(l>lx) add(--l);
        while(r<rx) add(++r);
        while(r>rx) del(r--);
        int x,y,z,w;
        x=sum1(a-1),y=sum1(b);
        z=sum2(a-1),w=sum2(b);
        ans1[q[i].id]=y-x;
        ans2[q[i].id]=w-z;
    }
    for(int i=1;i<=m;i++) printf("%d %d\n",ans1[i],ans2[i]);




	return 0;
}
/*

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值