bzoj 3236: [Ahoi2013]作业

易水人去,明月如霜。

思路

很容易看出来是莫队算法,但是对于后面结果的统计,如果直接
暴力的话肯定会超时,所以我们就考虑将其按照权值分块
题目中指出啦其每个位置的值是介于1~N所以我们就可以
分块来提高效率统计ans1的效率,对于ans2
我们可以思考维护一个权值的树状数组就好啦
代码:

#include <cstdio>
#include <cstring>
#include <iostream>
#include <cmath>
#include <algorithm>
#define N 100005
#define M 1000005
using namespace std;
int n,m,block,x[N],pos[N],ans1[M],ans2[M],num[N];
int g1[N];
int read()
{
    char ch;int s=0,f=1;ch=getchar();
    while(ch>'9'||ch<'0') { if(ch=='-') f*=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { s=s*10+ch-48;ch=getchar(); }
    return s*f;
}
struct node {
 int l,r,a,b,id;
 bool operator <(const node &t) const
 {
     if(pos[l]==pos[t.l])
        return pos[r]<pos[t.r];
     return pos[l]<pos[t.l];
 }
}q[M];
struct BIT{
 int c[N];
 inline int lowbit(int x){ return x&-x; }
 void add(int pos,int x)
 {
     while(pos<=n)
     {
         c[pos]+=x;pos+=lowbit(pos);
     }
 }
 int query(int pos)
 {
     int res=0;
     while(pos>0) res+=c[pos],pos-=lowbit(pos);
     return res;
 }
}t2;
void change(int t,int f)
{
    g1[pos[x[t]]]+=f;
    if(num[x[t]]==0&&f==1) t2.add(x[t],1);
    else if(num[x[t]]==1&&f==-1) t2.add(x[t],-1);
    num[x[t]]+=f;
}
void query(int id,int a,int b)
{
    if(pos[a]==pos[b])
    {
        for(int i=a;i<=b;i++)
            ans1[id]+=num[i];
        return ;
    }
    for(int i=a;i<=pos[a]*block;i++) ans1[id]+=num[i];
    for(int i=pos[a]+1;i<=pos[b]-1;i++) ans1[id]+=g1[i];
    for(int i=b;i>(pos[b]-1)*block;i--) ans1[id]+=num[i];
}
int main()
{
n=read(),m=read(); block=(int)sqrt(n);
 for(int i=1;i<=n;i++)
    x[i]=read(),pos[i]=(i-1)/block+1;
 for(int i=1;i<=m;i++)
    q[i].l=read(),q[i].r=read(),q[i].a=read(),q[i].b=read(),q[i].id=i;
 sort(q+1,q+1+m);
 int l=1,r=0;
 for(int i=1;i<=m;i++)
 {
        while(l<q[i].l)change(l++,-1);
        while(l>q[i].l)change(--l,1);
        while(r>q[i].r)change(r--,-1);
        while(r<q[i].r)change(++r,1);
           query(q[i].id,q[i].a,q[i].b);
        ans2[q[i].id]=t2.query(q[i].b)-t2.query(q[i].a-1);
 }
     for(int i=1;i<=m;i++)
    printf("%d %d\n",ans1[i],ans2[i]);
 return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值