BZOJ4939: [Ynoi2016]掉進兔子洞
阿新 • • 發佈:2018-12-16
題意
給你一個長度為 的數列,每次詢問三個區間共有的數的個數;
題解
對於這種求交或求並的問題,往往會先考慮到使用bitset,但是本題的資料範圍太大需要離散化,且bitset無法記錄數量,這裡就只能在離散化的時候用一點小技巧了:對於重複出現的數我們不需要unique,假如某數第一次出現的編號為 ,那麼第二次出現它的編號就可以設為 ,以此類推,對於這道題,每個區間獨立出來考慮,某數在一區間中第一次出現就用它的 編號,第二次出現就用 ,這樣就相當於把同樣的數看做了不同,bitset也就能發揮它的作用了;現在就有了這麼一個做法,如果能得到三個區間的數的集合,再 一下,就能知道共有的數的個數了,但是有多組詢問,該怎麼得到每一個詢問的三個區間的數集呢,考慮使用莫隊,每次區間擴大或縮小時我們都能只花費 的代價,就能再原區間的基礎上得到新的區間的數集,再把一個詢問看成三個區間分別詢問,最後再 到一起,便能輕鬆解決本題了;但是一算空間,超了怎麼辦?把 次詢問分三次來進行,這樣就只用一次性開 個bitset了,空間時間也容得下;但是注意離散化時不能加 ;
#include<bits/stdc++.h>
#define Fst first
#define Snd second
#define RG register
#define mp make_pair
#define mem(a,b) memset(a,b,sizeof(a))
using namespace std;
typedef long long LL;
typedef long double LD;
typedef unsigned int UI;
typedef unsigned long long ULL;
template<typename T> inline void read(T& x) {
char c = getchar();
bool f = false;
for (x = 0; !isdigit(c); c = getchar()) {
if (c == '-') {
f = true;
}
}
for (; isdigit(c); c = getchar()) {
x = x * 10 + c - '0';
}
if (f) {
x = -x;
}
}
template<typename T, typename... U> inline void read(T& x, U& ... y) {
read(x), read(y...);
}
const int N=1e5+10;
int n;
int W[N],V[N],CT[N],part[N],ANS[N/3],S[N],M[N];
bitset<N> G,F[N/3];
struct Data {
int l,r,id;
}Q[N];
bool cmp1(int A,int B) {
return W[A]<W[B];
}
bool cmp2(Data A,Data B) {
return part[A.l]==part[B.l]?A.r<B.r:part[A.l]<part[B.l];
}
void Move(int pos,int val) {
int t=M[pos];
if(val<0) G.reset(S[t]+CT[t]-1);
CT[t]+=val;
if(val>0) G.set(S[t]+CT[t]-1);
}
void Solve(int m) {
if(!m) return;
int cnt=0;
for(int i=1;i<=m;++i) {
F[i].set(); ANS[i]=0;
for(int j=0;j<3;++j) ++cnt,read(Q[cnt].l,Q[cnt].r),Q[cnt].id=i,ANS[i]+=Q[cnt].r-Q[cnt].l+1;
}
sort(Q+1,Q+cnt+1,cmp2);
G.reset(); mem(CT,0);
for(int i=1,L=1,R=0;i<=cnt;++i) {
while(R<Q[i].r) Move(++R,1);
while(R>Q[i].r) Move(R--,-1);
while(L<Q[i].l) Move(L++,-1);
while(L>Q[i].l) Move(--L,1);
F[Q[i].id]&=G;
}
for(int i=1;i<=m;++i) printf("%d\n",ANS[i]-3*(int)F[i].count());
}
//#define rua
int main() {
#ifdef rua
freopen("GG.out","w",stdout);
#endif
int m; read(n,m);
int Base=sqrt(n+1),cnt=1,ID=1;
for(int i=1;i<=n;++i) {
read(W[i]); part[i]=ID;
if(cnt==Base) cnt=1,++ID;
else ++cnt;
V[i]=i;
}
sort(V+1,V+n+1,cmp1);
for(int i=1,t=0;i<=n;++i) {
if(W[V[i]]!=W[V[i-1]]) ++t,S[t]=i;
M[V[i]]=t;
}
Solve(min(33334,m));
if(m>=33334) m-=33334;
else m=0;
Solve(min(33333,m));
if(m>=33333) m-=33333;
else m=0;
Solve(min(33333,m));
return 0;
}