hdu_5213_Lucky(莫队算法+容斥定理)

题目连接:hdu_5213_Lucky

题意:给你n个数,一个K,m个询问,每个询问有l1,r1,l2,r2两个区间,让你选取两个数x,y,x,y的位置为xi,yi,满足l1<=xi<=r1,l2<=y2<=r2,使得x+y=K;

题解:首先,这题没有修改操作,即可以离线,离线区间问题就要想到莫队算法,然后看状态怎么搞,因为要求的答案满足区间的可加性,我们令f(l,r)表示 l到r这个区间满足条件的ans,令F(l1,r1,l2,r2)为在这两个区间内选取的数满足条件的ans,则根据容斥定理,F(l1,r1,l2,r2)=f(l1,r2)-f(r1+1,r2)-f(l1,l2-1)+f(r1+1,l2-1)。这里为什么不用靠左的区间来减1呢?因为当靠左的区间为1时,减1会到0的位置,所以不方便操作,这个公式可以在草稿上画一下线段区间图就了解了。然后就是莫队的操作了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define F(i,a,b) for(int i=a;i<=b;i++)
using namespace std;

const int N=(int)3e4+7;
int sqr,n,a[N],m,K,l1,r1,l2,r2,ans[N],cnt[N];
struct dt{
	int l,r,id,f;
	bool operator<(const dt &b)const{
		if(l/sqr==b.l/sqr)return r<b.r;
		else return l/sqr<b.l/sqr;
	}
}q[N<<2];

void modui(){
	sqr=(int)sqrt(n+0.5);
	sort(q,q+(m<<2));
	int an=0,l=1,r=0;
	F(i,0,(m<<2)-1){
		while(r<q[i].r){
			r++;
			if(K>a[r]&&K-a[r]<=n)an+=cnt[K-a[r]];
			cnt[a[r]]++;
		}
		while(r>q[i].r){
			cnt[a[r]]--;
			if(K>a[r]&&K-a[r]<=n)an-=cnt[K-a[r]];
			r--;
		}
		while(l<q[i].l){
			cnt[a[l]]--;
			if(K>a[l]&&K-a[l]<=n)an-=cnt[K-a[l]];
			l++;
		}
		while(l>q[i].l){
			l--;
			if(K>a[l]&&K-a[l]<=n)an+=cnt[K-a[l]];
			cnt[a[l]]++;
		}
		ans[q[i].id]+=an*q[i].f;
	}
}

int main(){
	while(~scanf("%d",&n)){
		scanf("%d",&K);
		F(i,1,n)scanf("%d",a+i),cnt[i]=0;
		scanf("%d",&m);
		F(i,0,m-1){
			scanf("%d%d%d%d",&l1,&r1,&l2,&r2),ans[i]=0;
			q[(i<<2)].l=l1,q[(i<<2)].r=r2,q[(i<<2)].id=i,q[(i<<2)].f=1;
			q[(i<<2)+1].l=l1,q[(i<<2)+1].r=l2-1,q[(i<<2)+1].id=i,q[(i<<2)+1].f=-1;
			q[(i<<2)+2].l=r1+1,q[(i<<2)+2].r=r2,q[(i<<2)+2].id=i,q[(i<<2)+2].f=-1;
			q[(i<<2)+3].l=r1+1,q[(i<<2)+3].r=l2-1,q[(i<<2)+3].id=i,q[(i<<2)+3].f=1;
		}
		modui();
		F(i,0,m-1)printf("%d\n",ans[i]);
	}
	return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章