Luogu P2354 [NOI2014] 随机数生成器 题解 [ 蓝 ] [ 贪心 ] [ 均摊技巧 ]

随机数生成器:简单贪心加上均摊复杂度分析。

首先理解题意,容易发现我们依次尝试钦定值为 \(1,2,3,4,\dots\) 的格子在路径中,能满足的就优先满足,这样选一定是最优的。

考虑如何快速查询某个格子能否出现在路径中。观察可知,如果一个格子被选择,那么其左下角和右上角的部分便不能被选择了,因为人只能往右边和下边走。因此问题被转化为了一个矩形覆盖问题。

直接用线段树实现是显然不可过的,常数太大了。

于是继续观察:发现从某个格子向下走,如果遇到一个格子是不能被选择的,那么它下面的其他格子也都不能被选择了;向上走也是同理。

这样的单调性总格子数为 \(n^2\) 的限制,启发我们可以让每次覆盖操作都精准覆盖到某一个未曾覆盖的格子上,这样就可以用均摊复杂度完成矩形覆盖。实现上,可以维护一行中的左右端点,每次覆盖向上和向下走,遇到不能选择的就停下即可。

由于每个格子最多只被覆盖一次,所以均摊时间复杂度为 \(O(n^2)\)

#include <bits/stdc++.h>
#define fi first
#define se second
using namespace std;
typedef long long ll;
using pi=pair<short,short>;
ll x,a,b,c,d,n,m,q;
int t[25000005];
pi pos[25000005];
int l[5005],r[5005];
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>x>>a>>b>>c>>d>>n>>m>>q;
	for(int i=1;i<=n*m;i++)t[i]=i;
	for(int i=1;i<=n*m;i++)
	{
		x=(a*x%d*x%d+b*x%d+c)%d;
		swap(t[i],t[x%i+1]);
	}
	while(q--)
	{
		int u,v;
		cin>>u>>v;
		swap(t[u],t[v]);
	}
	int id=0;
	for(int i=1;i<=n;i++)
	{
		for(int j=1;j<=m;j++)
		{
			pos[t[++id]]={i,j};
		}
	}
	for(int i=1;i<=n;i++)r[i]=0x3f3f3f3f;
	for(int i=1;i<=n*m;i++)
	{
		int nx=pos[i].fi,ny=pos[i].se;
		if(ny>l[nx]&&ny<r[nx])
		{
			cout<<i<<" ";
			for(int j=nx+1;j<=n;j++)
			{
				if(l[j]>=ny-1)break;
				l[j]=ny-1;
			}
			for(int j=nx-1;j>=1;j--)
			{
				if(r[j]<=ny+1)break;
				r[j]=ny+1;
			}
		}
	}
	return 0;
}
posted @ 2025-05-15 00:16  KS_Fszha  阅读(17)  评论(0)    收藏  举报