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


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