bzoj 3236(莫队+分块)

传送门
题解:将权值进行分块,然后序列上进行莫队操作。查询时整块的就for块,块两边的for点。修改O(1),查询(√n)。
这种100sec的题跑久了无异于卡评测,所以建议使用读入优化,可以快接近1/3的时间

#include<bits/stdc++.h>
using namespace std;
#define pii pair<int ,int >
#define mp(x,y) make_pair(x,y)
const int MAXN=1e6+2;
int n,m,siz,a[MAXN];
int bel[MAXN],cnt[MAXN],num[MAXN],blo[MAXN];
pii ans[MAXN];
inline int read() {
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}
struct Q {
    int l,r,a,b,id;
    friend bool operator <(const Q &x,const Q &y) {
        return bel[x.l]^bel[y.l]?bel[x.l]<bel[y.l]:x.r<y.r;
    }
}q[MAXN];
inline void update(int pos,int delta) {
    int temp=a[pos];
    cnt[temp]+=delta,num[bel[temp]]+=delta;
    if (delta==-1&&!cnt[temp]) --blo[bel[temp]];
    if (delta==1&&cnt[temp]==1) ++blo[bel[temp]];
}
pii query(int st,int ed) {
    int fi=0,se=0;
    if (bel[st]==bel[ed]) {//in the same block
        for (int i=st;i<=ed;++i)
            if (cnt[i]) fi+=cnt[i],++se;
    }
    else {//across a few blocks
        for (int i=st;i<=bel[st]*siz;++i)
            if (cnt[i]) fi+=cnt[i],++se;
        for (int i=(bel[ed]-1)*siz+1;i<=ed;++i)
            if (cnt[i]) fi+=cnt[i],++se;
        for (int i=bel[st]+1;i<bel[ed];++i)
            if (num[i]) fi+=num[i],se+=blo[i];
    }
    return mp(fi,se);
}
int main() {
//  freopen("bzoj 3236.in","r",stdin);
    n=read(),m=read(),siz=(int)sqrt((double)n);
    for (register int i=1;i<=n;++i) a[i]=read(),bel[i]=(i-1)/siz+1;
    for (register 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+m+1);
    for (register int i=1,l=1,r=0;i<=m;++i) {
        while (l<q[i].l) update(l++,-1);
        while (r>q[i].r) update(r--,-1);
        while (l>q[i].l) update(--l,1);
        while (r<q[i].r) update(++r,1);
        ans[q[i].id]=query(q[i].a,q[i].b);
    }
    for (register int i=1;i<=m;++i) printf("%d %d\n",ans[i].first,ans[i].second);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值