題面傳送門:https://www.luogu.org/problemnew/show/P4688
(舒適提示,請直接翻至題目描述部分)c++
1e5的數據範圍,以及對區間每一個權值出現次數取min此類主席樹才能解決的操做會讓咱們想到莫隊;
三個區間取交集的操做會讓咱們想到bitset。
然而同個數值在多個位置出現須要分別計算,這點讓咱們對於使用bitset產生了懷疑。
但實際上咱們能夠利用一個小trick來解決這個問題,那就是利用不尋常的離散化,就是sort以後不用unique,讓bitset每一位不僅表明數值,還表明這個數值出現的次數。
這樣咱們莫隊的時候,對於當前區間維護一個桶cnt,記錄每一個離散化後的權值在此區間出現的次數,和一個bitset,維護權值及權值出現的次數信息。而後三個區間的bitset取交,再用三個區間的長度和減去交集1的個數就是答案。
具體實現請看代碼:spa
#include<bits/stdc++.h> using namespace std; #define rep(i,a,b) for(register int i=(a);i<=(b);++i) const int N =100505; const int M =33350; int n,m,blo[N],K,a[N],R; struct la{int l,r,id;}ask[N]; bitset<N> ans[M],now; int cnt[N],p[N],tot; bool cmp(la x,la y){ return blo[x.l]==blo[y.l]?x.r<y.r:blo[x.l]<blo[y.l]; } inline void del(int x){cnt[x]--;now[x-cnt[x]]=0;} inline void add(int x){now[x-cnt[x]]=1;cnt[x]++;} void doit(int y){ tot=0; rep(i,1,y)p[i]=0; rep(i,1,y){ ++tot;scanf("%d%d",&ask[tot].l,&ask[tot].r);p[i]+=ask[tot].r-ask[tot].l+1; ++tot;scanf("%d%d",&ask[tot].l,&ask[tot].r);p[i]+=ask[tot].r-ask[tot].l+1; ++tot;scanf("%d%d",&ask[tot].l,&ask[tot].r);p[i]+=ask[tot].r-ask[tot].l+1; ask[tot].id=ask[tot-1].id=ask[tot-2].id=i; } sort(ask+1,ask+tot+1,cmp); int l=1,r=0; rep(i,1,y)ans[i].set();//不用帶參,直接全置爲 1 rep(i,1,tot){ while(ask[i].l<l)add(a[--l]); while(ask[i].r>r)add(a[++r]); while(ask[i].l>l)del(a[l++]); while(ask[i].r<r)del(a[r--]); ans[ask[i].id]&=now; } while(l<=r)del(a[l++]); rep(i,1,y)printf("%d\n",p[i]-3*(int)ans[i].count()); } int main(){ scanf("%d%d",&n,&m);K=sqrt(n)+1; rep(i,1,n)scanf("%d",&a[i]),p[i]=a[i],blo[i]=(i-1)/K+1; sort(p+1,p+n+1); rep(i,1,n)a[i]=upper_bound(p+1,p+n+1,a[i])-p-1; R=m/3; if(R)doit(R); if(R)doit(R); doit(m-2*R); return 0; }