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;
}

浙公网安备 33010602011771号