[Ynoi2016]掉進兔子洞 題解
阿新 • • 發佈:2019-06-11
題面傳送門:https://www.luogu.org/problemnew/show/P4688
(溫馨提示,請直接翻至題目描述部分)
1e5的資料範圍,以及對區間每個權值出現次數取min此類主席樹才能解決的操作會讓我們想到莫隊;
三個區間取交集的操作會讓我們想到bitset。
然而同個數值在多個位置出現需要分別計算,這點讓我們對於使用bitset產生了懷疑。
但實際上我們可以利用一個小trick來解決這個問題,那就是利用不尋常的離散化,就是sort之後不用unique,讓bitset每一位不只代表數值,還代表這個數值出現的次數。
這樣我們莫隊的時候,對於當前區間維護一個桶cnt,記錄每個離散化後的權值在此區間出現的次數,和一個bitset,維護權值及權值出現的次數資訊。然後三個區間的bitset取交,再用三個區間的長度和減去交集1的個數就是答案。
#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;
}