1. 程式人生 > 實用技巧 >題解 掉進兔子洞

題解 掉進兔子洞

題目傳送門

題目大意

給出\(n\)個數,\(m\)次查詢,查詢互相獨立,每次查詢給出三個區間\([l1,r1],[l2,r2],[l3,r3]\),求出三個區間一個一個刪去相同數字之後剩餘數的個數。

思路

好秒啊!!!

首先我們可以離散化,但是與普通離散化不同的是,這裡離散化的結果實際上是小於等於這個數的個數。顯然這樣並不會影響結果。至於為什麼下面解釋。

我們考慮如何求出在三個區間都出現的數的個數。誒?似乎可以\(\texttt{bitset}\)+莫隊?但是我們發現我們沒有辦法統計出現次數誒?但是其實我們統計一下需要修改的點\(p\)已經出現的次數為\(cnt\),把\(bitset_{p-cnt}\)

設為\(1\)即可。至於為什麼呢?這是因為這樣做的話兩個不同數字顯然不會產生影響,這也是為什麼上面離散化的值取的是小於等於這個數的個數,而不是小於這個數的個數。

但是直接這樣做會爆空間,我們可以迴圈利用一下,查詢分成\(3\)次即可。

\(\texttt{Code}\)

#include <bits/stdc++.h>
using namespace std;

#define Int register int
#define MAXN 100005
#define MAXM 34010

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}

struct node{
	int l,r,t;
	void Putin(int g){read (l,r),t = g;}
}q[MAXM * 3]; 

bool cmp1 (node a,node b){return a.l < b.l;}
bool cmp2 (node a,node b){return a.r < b.r;}

map <int,int> mp;
bitset <MAXN> nb,ans[MAXM];
int n,m,tot = 1,a[MAXN],cnt[MAXN],nans[MAXM];

void ins (int p){nb[p - cnt[p]] = 1,cnt[p] ++;}
void del (int p){cnt[p] --,nb[p - cnt[p]] = 0;}

void Solve (){
	if (tot >= m) return ;int tp = 0,nl = 0,nr = 0;
	for (Int i = 1;i <= MAXM - 10 && tot <= m;++ tot,++ i){
		++ tp,q[tp].Putin(i),nans[i] += q[tp].r - q[tp].l + 1;
		++ tp,q[tp].Putin(i),nans[i] += q[tp].r - q[tp].l + 1;
		++ tp,q[tp].Putin(i),nans[i] += q[tp].r - q[tp].l + 1;
	}
	for (Int i = 1;3 * i <= tp;++ i) ans[i].set ();sort (q + 1,q + tp + 1,cmp1);
	for (Int i = 1;i <= tp;i += 320){
		int r = min (i + 319,tp);
		sort (q + i,q + r + 1,cmp2);
	}
	for (Int i = 1;i <= tp;++ i){
		if (nr < q[i].l){
			for (Int j = nl;j <= nr;++ j) del (a[j]);nl = q[i].l,nr = q[i].r;
			for (Int j = nl;j <= nr;++ j) ins (a[j]);
		}
		else{
			while (nl < q[i].l) del (a[nl ++]);
			while (nl > q[i].l) ins (a[-- nl]);
			while (nr < q[i].r) ins (a[++ nr]);
			while (nr > q[i].r) del (a[nr --]);
		}
		ans[q[i].t] &= nb;
	}
	for (Int i = nl;i <= nr;++ i) del (a[i]);
	for (Int i = 1;3 * i <= tp;++ i) write (nans[i] - (int)ans[i].count () * 3),putchar ('\n'),nans[i] = 0;
}

signed main(){
	read (n,m);
	for (Int i = 1;i <= n;++ i) read (a[i]),mp[a[i]] ++;map <int,int>::iterator it,it1;
	for (it = mp.begin(),it1 = it,it1 ++;it1 != mp.end();++ it,++ it1) it1 -> second += it -> second;
	for (Int i = 1;i <= n;++ i) a[i] = mp[a[i]];
	Solve (),Solve (),Solve ();
	return 0;
}
```題解 掉進兔子洞