LOJ#2254. 「SNOI2017」一个简单的询问 莫队
求 $,\sum_{x=0}^{\infty} get(l,r,x)get(l_{2},r_{2},x),q$ 组询问.
由于这里的 $get$ 会涉及到两个区间,所以十分不好处理.
那么我们就要想办法把这个式子转化成只和一个区间有关的东西.
考虑拆成前缀和,即 $(s[r]-s[l-1])\times (s[r_{2}]-s[l_{2}-1])$
$\Rightarrow s[r]s[r_{2}]-s[r]s[l_2-1]-s[l-1]s[r_2]+s[l-1]s[l_2-1]$
然后我们把这 4 个值看成是新的 4 个区间,用莫队解一下就行了.
code:
#include <bits/stdc++.h> #define N 50009 #define ll long long #define setIO(s) freopen(s".in","r",stdin) using namespace std; int a[N],n,B,m; ll tot,sum[2][N],Ans[N]; struct que { int l,r,id,op; que(int l=0,int r=0,int id=0,int op=0):l(l),r(r),id(id),op(op){} bool operator<(const que b) const { return (l/B)==(b.l/B)?r<b.r:(l/B)<(b.l/B); } }q[N<<2]; void add(int x,int o) { tot-=sum[o][x]*sum[o^1][x]; ++sum[o][x]; tot+=sum[o][x]*sum[o^1][x]; } void del(int x,int o) { tot-=sum[o][x]*sum[o^1][x]; --sum[o][x]; tot+=sum[o][x]*sum[o^1][x]; } int main() { // setIO("input"); int l=1,r=1,cnt=0,x,y,z; scanf("%d",&n),B=sqrt(n); for(int i=1;i<=n;++i) scanf("%d",&a[i]); scanf("%d",&m); for(int i=1;i<=m;++i) { int l1,r1,l2,r2; scanf("%d%d%d%d",&l1,&r1,&l2,&r2); q[++cnt]=que(min(r1,r2),max(r1,r2),i,1); if(l2>1) q[++cnt]=que(min(l2-1,r1),max(l2-1,r1),i,-1); if(l1>1) q[++cnt]=que(min(l1-1,r2),max(l1-1,r2),i,-1); if(l1>1&&l2>1) q[++cnt]=que(min(l1-1,l2-1),max(l1-1,l2-1),i,1); } sort(q+1,q+1+cnt); sum[0][a[1]]=sum[1][a[1]]=1,tot=1; for(int i=1;i<=cnt;++i) { while(r<q[i].r) add(a[++r],0); while(l<q[i].l) add(a[++l],1); while(l>q[i].l) del(a[l--],1); while(r>q[i].r) del(a[r--],0); Ans[q[i].id]+=q[i].op*tot; } for(int i=1;i<=m;++i) printf("%lld\n",Ans[i]); return 0; }