【Gym-101889 D】Daunting device【分塊】

題意:

長度爲 nn 的序列,每個點都有一個顏色,一共有 CC 個顏色,支持兩種操作,第一種給出 l r xl\ r\ x,將區間 [l,r][l,r] 全部染成 xx,第二種給出一個 xx,詢問 xx 顏色一共在序列中出現了多少次。

所有操作結束後,還要查詢每種顏色出現的次數,給出最多出現的次數。(1n,C,m105)(1\leq n,C,m\leq 10^5)


思路:

非常明顯的區間推平操作,應該使用珂朵莉樹進行解決,即用 setset 維護每一塊相同區域。

老司機樹是可以的,但是並沒有採用,還是使用了分塊的方法,雖然 TT 的飛起…一開始的想法是給每個塊記一個 mapmap,標記每個塊中各個數字出現的次數,如果整個塊被賦值了,就 map.clear()map.clear()。但是 TT 的不行,第 6363 個點怎麼也過不去…

後來採用了完全的暴力分塊,即每個塊打個 lazylazy,如果 lazylazy 不爲 00,則整個塊中所有點大小均爲 lazylazy。如果 lazylazy00,則暴力修改、查詢。那麼如何分析這個做法的時間複雜度呢?

一共兩種操作,修改操作每次最多涉及 n\sqrt n 個塊,如果塊被完全覆蓋,O(1)O(1) 打上標記,如果塊只被部分覆蓋,下放標記 O(n)O(\sqrt n),修改點值 O(n)O(\sqrt n),由於每次最多隻有 22 個塊被部分覆蓋,最多 n\sqrt n 個塊被完全覆蓋,因此修改操作總時間複雜度爲 O(n)O(\sqrt n)

而對於查詢操作,如果該塊被打上了標記,則查詢複雜度爲 O(1)O(1),如果沒有打上標記,則直接暴力查詢,時間複雜度爲 O(n)O(\sqrt n)。此題是修改與查詢次數一致,而修改操作每次最多使得 22 個塊標記消失。因此可以發現,我們可以故意設置數據使得此種做法 TLETLE,即利用修改操作使得所有塊的 lazylazy 標記消失,然後每次查詢顏色時就會遍歷所有塊,可以將時間複雜度卡成 O(n2)O(n^2)。(不過此題數據較水,此種方法也可以過,而且很快,390 ms390\ ms 即可通過…)

因此正確的做法是當塊沒有 lazylazy 標記時,爲了避免直接暴力詢問,我們對於每個塊記錄一個 unordered_mapunordered\_map,查詢複雜度爲 O(1)O(1)。修改複雜度會變成 O(nlog(n))O(\sqrt n*log(n)),查詢複雜度爲 O(n)O(\sqrt n),但是避免了被卡的可能。(790 ms790\ ms 可以通過)


總結:

對於一些不太常見的數據結構問題,一定要嘗試考慮分塊,因爲分塊方法用途很多,而且算法核心是暴力。

而對於分塊算法,一定要多多嘗試,你可能覺得這樣分塊會被你故意構造的一組數據卡成 O(n2)O(n^2),但是出題人未必就考慮到了你的這種做法,因此一定要多嘗試,不要只去嘗試那些 O(nlognn)O(n*logn*\sqrt n) 的算法。當然如果能直接想到 O(n)O(\sqrt n) 的算法則是最好的!


代碼1:(390 ms)

#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a);
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define per(i,a,b) for(int i = a; i >= b; i--)
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
typedef long long ll;
typedef double db;
const int N = 1e5+10;
const db EPS = 1e-9;
using namespace std;
 
void dbg() {cout << "\n";}
template<typename T, typename... A> void dbg(T a, A... x) {cout << a << ' '; dbg(x...);}
#define logs(x...) {cout << #x << " -> "; dbg(x);}
 
int n,m,a[N],L[N],R[N],pos[N],cnt,sz,lazy[N],num[N];
unordered_map<int,int> mp[1010];
 
void change(int l,int r,int x){
	rep(i,pos[l],pos[r]){
		if(L[i] >= l && R[i] <= r){
			// mp[i].clear();
			// mp[i][x] = R[i]-L[i]+1;
			lazy[i] = x;
		}
		else{
			if(lazy[i]){
				rep(j,L[i],R[i]) a[j] = lazy[i];
				lazy[i] = 0;
			}
			rep(j,max(l,L[i]),min(R[i],r)){
				// mp[i][a[j]]--;
				// mp[i][x]++;
				a[j] = x;
			}
		}
	}
}
 
int ask(int x){
	int ans = 0;
	rep(i,1,cnt){
		if(lazy[i]){
			if(lazy[i] == x) ans += R[i]-L[i]+1;
		}
		else{
			rep(j,L[i],R[i])
				if(a[j] == x) ans++;
		}
	}
	return ans;
}
 
int main()
{
	int h; scanf("%d%d%d",&n,&h,&m);
	sz = sqrt(n);
	cnt = n/sz;
	if(n%sz != 0) cnt++;
	rep(i,1,cnt){
		L[i] = sz*(i-1)+1;
		R[i] = min(sz*i,n);
		mp[i][1] = R[i]-L[i]+1;
	}
	rep(i,1,n) pos[i] = ((i-1)/sz)+1;
	rep(i,1,n) a[i] = 1;
 
	while(m--){
		ll p,x,a,b;
		scanf("%lld%lld%lld%lld",&p,&x,&a,&b);
		ll s = ask(p);
		ll m1 = (a+s*s)%n;
		ll m2 = (a+(s+b)*(s+b))%n;
		change(min(m1,m2)+1,max(m1,m2)+1,x);
	}
	rep(i,1,cnt)
		if(lazy[i]){
			rep(j,L[i],R[i]) a[j] = lazy[i];
			lazy[i] = 0;
		}
	rep(i,1,n)
		num[a[i]]++;
	int maxx=0;
	rep(i,1,h)
		maxx = max(maxx,num[i]);
	printf("%d\n",maxx);
	return 0;	
}

代碼2:(790 ms)

#include <bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a);
#define rep(i,a,b) for(int i = a; i <= b; i++)
#define per(i,a,b) for(int i = a; i >= b; i--)
#define __ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
typedef long long ll;
typedef double db;
const int N = 1e5+10;
const db EPS = 1e-9;
using namespace std;
 
void dbg() {cout << "\n";}
template<typename T, typename... A> void dbg(T a, A... x) {cout << a << ' '; dbg(x...);}
#define logs(x...) {cout << #x << " -> "; dbg(x);}
 
int n,m,a[N],L[N],R[N],pos[N],cnt,sz,lazy[N],num[N];
unordered_map<int,int> mp[510];
 
void change(int l,int r,int x){
	rep(i,pos[l],pos[r]){
		if(L[i] >= l && R[i] <= r) lazy[i] = x;
		else{
			if(lazy[i]){
				std::unordered_map<int,int> tmp;
				mp[i].swap(tmp); //設置一個空的交換,類似於clear
				mp[i].reserve(1000); //預留map中元素個數
				mp[i].rehash(1000); //給元素個數預留足夠的bucket用於hash
				mp[i][lazy[i]] = R[i]-L[i]+1;
				rep(j,L[i],R[i]) a[j] = lazy[i];
				lazy[i] = 0;
			}
			rep(j,max(l,L[i]),min(R[i],r)){
				mp[i][a[j]]--;
				mp[i][x]++;
				a[j] = x;
			}
		}
	}
}
 
int ask(int x){
	int ans = 0;
	rep(i,1,cnt){
		if(lazy[i]){
			if(lazy[i] == x) ans += R[i]-L[i]+1;
		}
		else ans += mp[i][x];
	}
	return ans;
}
 
int main()
{
	int h; scanf("%d%d%d",&n,&h,&m);
	sz = sqrt(n);
	cnt = n/sz;
	if(n%sz != 0) cnt++;
	rep(i,1,cnt){
		L[i] = sz*(i-1)+1;
		R[i] = min(sz*i,n);
		mp[i][1] = R[i]-L[i]+1;
	}
	rep(i,1,n) pos[i] = ((i-1)/sz)+1;
	rep(i,1,n) a[i] = 1;
 
	while(m--){
		ll p,x,a,b;
		scanf("%lld%lld%lld%lld",&p,&x,&a,&b);
		ll s = ask(p);
		ll m1 = (a+s*s)%n;
		ll m2 = (a+(s+b)*(s+b))%n;
		change(min(m1,m2)+1,max(m1,m2)+1,x);
	}
	rep(i,1,cnt)
		if(lazy[i]){
			rep(j,L[i],R[i]) a[j] = lazy[i];
				lazy[i] = 0;
		}
	rep(i,1,n)
		num[a[i]]++;
	int maxx=0;
	rep(i,1,h)
		maxx = max(maxx,num[i]);
	printf("%d\n",maxx);
	return 0;	
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章