【BZOJ3671】【UOJ6】【NOI2014】随机数生成器

【题目链接】

【思路要点】

  • 首先,按照题目给定的方式生成矩阵(注意避免不必要的取模,否则会因此超时)。
  • 从小到大考虑每一个数,如果能够将某个数加入路径,那么我们一定要将其加入,并且,其左下、右上两个矩形的数字就会因此不能在以后被加入路径。
  • 考虑不能被加入路径的数,它们构成了两个轮廓线单调的联通块。由于轮廓线的单调性,我们可以暴力将每个不能被加入的数字标记,当遇到已经被标记的数字退出循环即可。
  • 显然每个数字只会被标记一次,一共会向路径中加入\(O(N+M)\)个数字,每次加入一个数字至多会访问\(O(N)\)个已经被标记的元素。所以总时间复杂度为\(O(NM+Q)\)

【代码】

#include<bits/stdc++.h>
using namespace std;
const int MAXN = 5005;
template <typename T> void read(T &x) {
	x = 0; int f = 1;
	char c = getchar();
	for (; !isdigit(c); c = getchar()) if (c == '-') f = -f;
	for (; isdigit(c); c = getchar()) x = x * 10 + c - '0';
	x *= f;
}
template <typename T> void write(T x) {
	if (x < 0) x = -x, putchar('-');
	if (x > 9) write(x / 10);
	putchar(x % 10 + '0');
}
template <typename T> void writeln(T x) {
	write(x);
	puts("");
}
long long seed;
int a, b, c, d, n, m, q, cnt, ans[MAXN * 2];
int val[MAXN * MAXN], home[MAXN * MAXN];
void rnd() {seed = (seed * seed * a + seed * b + c) % d; }
int f(int x, int y) {return (x - 1) * m + y; }
int main() {
	read(seed), read(a), read(b), read(c), read(d);
	read(n), read(m), read(q);
	for (int i = 1; i <= n * m; i++)
		val[i] = i;
	for (int i = 1; i <= n * m; i++)
		rnd(), swap(val[i], val[seed % i + 1]);
	for (int i = 1; i <= q; i++) {
		int x, y; read(x), read(y);
		swap(val[x], val[y]);
	}
	for (int i = 1; i <= n * m; i++)
		home[val[i]] = i;
	for (int i = 1; i <= n * m; i++) {
		if (val[home[i]] == -1) continue;
		ans[++cnt] = i;
		int x = (home[i] - 1) / m + 1;
		int y = (home[i] - 1) % m + 1;
		for (int j = x - 1; j >= 1; j--) {
			if (y == m || val[f(j, y + 1)] == -1) break;
			for (int k = y + 1; k <= m; k++)
				if (val[f(j, k)] == -1) break;
				else val[f(j, k)] = -1;
		}
		for (int j = x + 1; j <= n; j++) {
			if (y == 1 || val[f(j, y - 1)] == -1) break;
			for (int k = y - 1; k >= 1; k--)
				if (val[f(j, k)] == -1) break;
				else val[f(j, k)] = -1;
		}
	}
	for (int i = 1; i <= cnt - 1; i++)
		printf("%d ", ans[i]);
	writeln(ans[cnt]); 
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值