Strange Queries (莫队+容斥原理)

题目传送门

题目大意:

给你一个长度为\(N\)的数列,求满足\(a_i = a_j\)\(l1\leq i \leq r1 , l2 \leq j \leq r2\)\((i,j)\)对数。

看到这种题目,第一反应就是莫队啦,但一看这个询问有点奇怪,不好算,怎么办呢?

这个时候就要用到容斥原理

至于怎么容斥,学过容斥原理的就可以跳过了。

首先:我们要求的答案一定\([l1,r2]\)(假设\(r1 < r2\))区间之内。很明显我们在这个区间内有许多答案是不需要的,那么就需要减去,比如\([l1,r1]\)自己配对或\([l1,r1]\)\([r1,l2]\)的配对,减去这些之后,我们发现区间\([r1,l2]\)会被减去两次,所以要加上来。

然后,我们就得到了美丽的关系式:

\[Ans = ans[l1,r2] - ans[l1,l2] - ans[r1,r2] + ans[r1,l2] \]

为了保证复杂度,我们要把四个$ ans $放在四个询问中,所以要开四倍。

\(\mathcal{Code}\)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define get ch=getchar()
#define isd (ch>=48&&ch<=57)
#define ll long long
#define Re register
#define In inline
using namespace std;
const int maxn=5e5+1;
In int read(){
	char get;int r=0,s=1;
	while(!isd)s=ch==45?0:s,get;while(isd)r=(r<<1)+(r<<3)+(ch^48),get;
	return s?r:-r;
}

struct Q{int l,r,id;}q[maxn<<2];
int n,m,M,u,L,R;
ll Ans;
int a[maxn],pos[maxn];
ll ans[maxn],s[maxn];

bool cmp(Q a,Q b){return pos[a.l]==pos[b.l]?a.r<b.r:a.l<b.l;}

void add(int p){Ans+=s[a[p]]++;return;}

void sub(int p){Ans-=--s[a[p]];return;}

int main() {
	n=read();u=sqrt(n);
    for(Re int i=1;i<=n;i++)a[i]=read();
    for(Re int i=1;i<=n;i++)pos[i]=i/u+1;
	M=read();
    for(Re int i=1,a,b,c,d;i<=M;i++){
		a=read(),b=read()+1,c=read()-1,d=read();
        q[++m]=(Q){a,d,i};q[++m]=(Q){a,c,-i};
        q[++m]=(Q){b,d,-i};q[++m]=(Q){b,c,i};
    }
    sort(q+1,q+m+1,cmp);
	L=n+1,R=n;
    for(Re int i=1;i<=m;i++){
        while(L<q[i].l)sub(L++);
        while(L>q[i].l)add(--L);
        while(R>q[i].r)sub(R--);
        while(R<q[i].r)add(++R);
        if(q[i].id>0)ans[q[i].id]+=Ans;
        else ans[-q[i].id]-=Ans;
    }
    for(int i=1;i<=M;i++)printf("%lld\n",ans[i]);
    return 0;
}
posted @ 2018-12-22 13:17  绥棱泷Narcissus  阅读(158)  评论(0编辑  收藏  举报