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 "; #else const char LL[]="%lld "; #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; }