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

题意:

长度为 n n n 的序列,每个点都有一个颜色,一共有 C C C 个颜色,支持两种操作,第一种给出 l   r   x l\ r\ x l r x,将区间 [ l , r ] [l,r] [l,r] 全部染成 x x x,第二种给出一个 x x x,询问 x x x 颜色一共在序列中出现了多少次。

所有操作结束后,还要查询每种颜色出现的次数,给出最多出现的次数。 ( 1 ≤ n , C , m ≤ 1 0 5 ) (1\leq n,C,m\leq 10^5) (1n,C,m105)


思路:

非常明显的区间推平操作,应该使用珂朵莉树进行解决,即用 s e t set set 维护每一块相同区域。

老司机树是可以的,但是并没有采用,还是使用了分块的方法,虽然 T T T 的飞起…一开始的想法是给每个块记一个 m a p map map,标记每个块中各个数字出现的次数,如果整个块被赋值了,就 m a p . c l e a r ( ) map.clear() map.clear()。但是 T T T 的不行,第 63 63 63 个点怎么也过不去…

后来采用了完全的暴力分块,即每个块打个 l a z y lazy lazy,如果 l a z y lazy lazy 不为 0 0 0,则整个块中所有点大小均为 l a z y lazy lazy。如果 l a z y lazy lazy 0 0 0,则暴力修改、查询。那么如何分析这个做法的时间复杂度呢?

一共两种操作,修改操作每次最多涉及 n \sqrt n n 个块,如果块被完全覆盖, O ( 1 ) O(1) O(1) 打上标记,如果块只被部分覆盖,下放标记 O ( n ) O(\sqrt n) O(n ),修改点值 O ( n ) O(\sqrt n) O(n ),由于每次最多只有 2 2 2 个块被部分覆盖,最多 n \sqrt n n 个块被完全覆盖,因此修改操作总时间复杂度为 O ( n ) O(\sqrt n) O(n )

而对于查询操作,如果该块被打上了标记,则查询复杂度为 O ( 1 ) O(1) O(1),如果没有打上标记,则直接暴力查询,时间复杂度为 O ( n ) O(\sqrt n) O(n )。此题是修改与查询次数一致,而修改操作每次最多使得 2 2 2 个块标记消失。因此可以发现,我们可以故意设置数据使得此种做法 T L E TLE TLE,即利用修改操作使得所有块的 l a z y lazy lazy 标记消失,然后每次查询颜色时就会遍历所有块,可以将时间复杂度卡成 O ( n 2 ) O(n^2) O(n2)。(不过此题数据较水,此种方法也可以过,而且很快, 390   m s 390\ ms 390 ms 即可通过…)

因此正确的做法是当块没有 l a z y lazy lazy 标记时,为了避免直接暴力询问,我们对于每个块记录一个 u n o r d e r e d _ m a p unordered\_map unordered_map,查询复杂度为 O ( 1 ) O(1) O(1)。修改复杂度会变成 O ( n ∗ l o g ( n ) ) O(\sqrt n*log(n)) O(n log(n)),查询复杂度为 O ( n ) O(\sqrt n) O(n ),但是避免了被卡的可能。( 790   m s 790\ ms 790 ms 可以通过)


总结:

对于一些不太常见的数据结构问题,一定要尝试考虑分块,因为分块方法用途很多,而且算法核心是暴力。

而对于分块算法,一定要多多尝试,你可能觉得这样分块会被你故意构造的一组数据卡成 O ( n 2 ) O(n^2) O(n2),但是出题人未必就考虑到了你的这种做法,因此一定要多尝试,不要只去尝试那些 O ( n ∗ l o g n ∗ n ) O(n*logn*\sqrt n) O(nlognn ) 的算法。当然如果能直接想到 O ( n ) O(\sqrt n) O(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;	
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Gene_INNOCENT

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值