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

  

posted @ 2020-06-09 16:54  EM-LGH  阅读(157)  评论(0编辑  收藏  举报