bzoj 2054/2375. 疯狂的涂色

bzoj 2054/bzoj 2375

Description

给定一个长度为 \(n\) 的序列,共 \(m\) 次操作,再给定两个数 \(p\)\(q\),每次把 \((i*p+q)\)%\(n+1\)\((i*q+p)\)%\(n+1\) 之间的点染上颜色 \(i\) ,被染过色的会被新颜色覆盖,求最后每个点的颜色。

\(n,m≤10^7\)

Solution

Solution 1 \(TLE\) code

我会暴力!

暴力从第1次操作到第m次往后覆盖,最后直接输出。

时间复杂度 \(O(mn)\)似乎一个点过不掉。

Solution 2 \(TLE\) code

考虑最简单的优化。

与铺地毯相同,这题也是只要求出最后的状态。

所以可以从后往前枚举,每次只对区间中没有染过色的点进行染色,染过的就不用管了,染到每个位置全部有色为止。

最坏时间复杂度:\(O(mn)\),可以水过一点数据。

轻易卡掉这个算法的数据:\(m\) 开到很大且其中有一个格子始终没有被涂上色。

Solution 3 \(AC\)

此题m巨大,肯定不能挨个处理一遍。考虑能不能在算法2的基础上再优化。

发现在算法2中,每次寻找没有染过色的店是直接暴力查找,这样很浪费时间。

设一个并查集,\(fa[i] = x\) 定义为指向第 \(i\)号格子后面下一个没有涂色格子的位置为 \(x\)

仍然是倒序枚举,如果这次要把第 \(x\) 号格子给染了,那就指向后面一个位置的 \(fa\) 值(即指向 \(find(x + 1)\))。

最后就是如何判断是否全部染色完了。可以新增一个计数器,每染一个点就 \(+1\),加到 \(n\) 就退出。

时间复杂度:\(O(M)\)

Code

#include <bits/stdc++.h>
using namespace std;
const int M = 1E6 + 5;
int n, m, p, q;
int b[M];//b数组表示每个格子染色的状态
int fa[M];
int find(int x)
{
	if(fa[x] == x)
	{
		return x;
	}
	else
	{
		return fa[x] = find(fa[x]);
	}
}
int main()
{
	cin >> n >> m >> p >> q;
	int cnt = 0;
	for(int i = 1; i <= n + 1; i++)
	{
		fa[i] = i;
	}
	for(int i = m; i >= 1; i--)
	{
		int l = (p * i + q) % n + 1;
		int r = (q * i + p) % n + 1;//根据题意计算区间左右端点
		if(l > r)
		{
			swap(l, r);
		}
		for(int j = find(l); j <= r; j = find(j))//只要在这个区间内就不停向后找未染色的点
		{

			b[j] = i;
			fa[j] = j + 1;
			cnt++;
		}
		if(cnt == n)//如果全部染好色了提前退出
		{
			break;
		} 
	}
	for(int i = 1; i <= n; i++)
	{
		printf("%d\n", b[i]);
	}
}
posted @ 2021-08-04 15:12  panjx  阅读(49)  评论(0编辑  收藏  举报