【HDU5213 BestCoder Round 39D】【莫隊演算法+容斥】Lucky 兩個區間各選一個數使得和為K的方案數
阿新 • • 發佈:2019-02-14
#include<stdio.h> #include<string.h> #include<ctype.h> #include<math.h> #include<iostream> #include<string> #include<set> #include<map> #include<vector> #include<queue> #include<bitset> #include<algorithm> #include<time.h> using namespace std; void fre(){freopen("c://test//input.in","r",stdin);freopen("c://test//output.out","w",stdout);} #define MS(x,y) memset(x,y,sizeof(x)) #define MC(x,y) memcpy(x,y,sizeof(x)) #define MP(x,y) make_pair(x,y) #define ls o<<1 #define rs o<<1|1 typedef long long LL; typedef unsigned long long UL; typedef unsigned int UI; template <class T1,class T2>inline void gmax(T1 &a,T2 b){if(b>a)a=b;} template <class T1,class T2>inline void gmin(T1 &a,T2 b){if(b<a)a=b;} const int N=3e4+10,M=12e4+10,Z=1e9+7,ms63=1061109567; int casenum,casei; int n,m,K,g; int l1,r1,l2,r2; struct A { int o,v,id,l,r; A(){} A(int o_,int v_,int id_,int l_,int r_){o=o_;v=v_;id=id_;l=l_;r=r_;} bool operator < (const A& b)const { if(id!=b.id)return id<b.id; return r<b.r; } }a[M]; int v[N]; int num[N]; int ans[N]; inline int ins(int p) { if(v[p]<K) { ++num[v[p]]; return num[K-v[p]]; } else return 0; } inline int del(int p) { if(v[p]<K) { --num[v[p]]; return num[K-v[p]]; } else return 0; } int main() { while(~scanf("%d%d",&n,&K)) { int len=sqrt(n); for(int i=1;i<=n;i++) { scanf("%d",&v[i]); num[i]=0; } scanf("%d",&m); g=0;for(int i=1;i<=m;i++) { scanf("%d%d%d%d",&l1,&r1,&l2,&r2); a[i].id=a[i].l/len; a[++g]=A(i,1,l1/len,l1,r2); a[++g]=A(i,-1,l1/len,l1,l2-1); a[++g]=A(i,-1,(r1+1)/len,r1+1,r2); if(r1+1<=l2-1)a[++g]=A(i,1,(r1+1)/len,r1+1,l2-1); ans[i]=0; } sort(a+1,a+g+1); int ANS=0; int l=a[1].l; int r=a[1].l-1; for(int i=1;i<=g;i++) { while(l>a[i].l)ANS+=ins(--l); while(r<a[i].r)ANS+=ins(++r); while(l<a[i].l)ANS-=del(l++); while(r>a[i].r)ANS-=del(r--); ans[a[i].o]+=ANS*a[i].v; } for(int i=1;i<=m;i++)printf("%d\n",ans[i]); } return 0; } /* 【trick&&吐槽】 果然題目難度都只不過是一步步升級而來的呀。 會了莫隊之後,只要再學會容斥一下這道題就能做出來了哇! 加油~ 【題意】 T(5)組資料 每組資料給你n(1<=n<=30000)個數字,每個數字都在[1,n]之間。 並且有m(1<=m<=30000)個詢問。 還告訴你一個數字K(2<=k<=2n且k為奇數)。 對於第i個詢問,給你2個區間, [l1~r1] [l2~r2],資料保證1<=l1<=r1<l2<=r2<=n 讓你求出有多少對pair(a[x],a[y]),使得—— a[x]在[l1,r1],a[y]在[l2,r2]且a[x]+a[y]==K. 【型別】 莫隊演算法 【分析】 這道題設計到區間詢問,而且可以離線處理。於是我們很自然地想到莫隊演算法。 我們發現數字的範圍很小,於是我們可以直接計數1~n的數分別是多少個。 然後因為K為奇數,所以就自然不會需要考慮一個數和自己自成pair。 這道題有一個需要處理的問題,就是一般的莫隊是隻有一個區間,而這道題卻有兩個區間,該怎麼辦? 於是我們還需要——容斥。 我們定義符號f(a,b)表示詢問區間為a,b下的答案, 其實更準確的意思是,在這個區間選擇兩個為a[i],a[j],且i<j的,且a[i]+a[j]==K的方案數 並且用+表示集合的並。 對於f(a,a),我們可以用莫隊演算法很容易地求得。 我們定義a=(l1,r1),b=(l2,r2),c=(r1+1,l2-1), (這道題的資料保證了1<=l1<=r1<l2<=r2<=n,於是就不會出現f()內區間的右界小於左界的情況。) 那麼f(a,b)則可以拆分成f(a+c+b,a+c+b)-f(a+c,a+c)-f(c+b,c+b)+f(c,c)。 (ps:這個容斥可以通過畫一個3*3的矩陣來理解哦~) 於是一個詢問就被我們拆成了4部分,套下莫隊演算法,這道題就可以輕鬆AC啦! 【時間複雜度&&優化】 O(msqrt(n)) */