hdu_5213_Lucky(莫隊演算法+容斥定理)
阿新 • • 發佈:2019-01-31
題意:給你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; }