BZOJ5016 Snoi2017一个简单的询问(莫队)

  容易想到区间转化成前缀和。这样每个询问有了二维坐标,莫队即可。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 50010
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
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<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
int n,m,a[N],cntx[N],cnty[N],t,block;
ll ans[N];
struct data
{
    int k,x,y,i,op;
    bool operator <(const data&a) const
    {
        return k<a.k||k==a.k&&(k&1?y>a.y:y<a.y);
    }
}q[N<<2];
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj5016.in","r",stdin);
    freopen("bzoj5016.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    n=read();
    for (int i=1;i<=n;i++) a[i]=read();
    m=read();block=sqrt(n);
    for (int i=1;i<=m;i++)
    {
        int l1=read(),r1=read(),l2=read(),r2=read();
        t++,q[t].x=r1,q[t].y=r2,q[t].i=i,q[t].op=1,q[t].k=q[t].x/block;
        t++,q[t].x=r1,q[t].y=l2-1,q[t].i=i,q[t].op=-1,q[t].k=q[t].x/block;
        t++,q[t].x=l1-1,q[t].y=r2,q[t].i=i,q[t].op=-1,q[t].k=q[t].x/block;
        t++,q[t].x=l1-1,q[t].y=l2-1,q[t].i=i,q[t].op=1,q[t].k=q[t].x/block;
    }
    sort(q+1,q+t+1);
    int x=0,y=0;ll cur=0;
    for (int i=1;i<=t;i++)
    {
        while (y<q[i].y) y++,cnty[a[y]]++,cur+=cntx[a[y]];
        while (y>q[i].y) cur-=cntx[a[y]],cnty[a[y]]--,--y;
        while (x<q[i].x) x++,cntx[a[x]]++,cur+=cnty[a[x]];
        while (x>q[i].x) cur-=cnty[a[x]],cntx[a[x]]--,--x;
        ans[q[i].i]+=q[i].op*cur;
    }
    for (int i=1;i<=m;i++) printf(LL,ans[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Gloid/p/10063506.html