题意:按某种规则生成1~N*M的一个排列,填进N*M矩阵,从左上角走到右下角,将经过的数从小到大排序,求排序后字典序最小的序列。
1一定是在这个序列里的。2在里面吗?取决于它是否在1的左上方或右下方。
开始,我直接递归,以为总共O(MN)(后面还排了一次序,不只这个复杂度),但是不确定,果然。UOJ上有大样例,测了一下,11s??加了几处register
,变3s,便提交试试。TLE,30分。并不是每个点都只访问常数次。
题面中那些生成随机序列的步骤是废话吗?还能提取出什么信息吗?
这是一个1~N*M的排列!为何不从1到N*M枚举呢?每加入一个数,就把它的左下方和右上方删除。如果碰到已经删除的点就停止,这样,便保证了均摊O(1)的复杂度。
这道题的内存有点卡,实现的时候要小心啊……
#include <cstdio>
#include <algorithm>
#define row(k) (((k)-1)/m+1)
#define col(k) (((k)-1)%m+1)
using namespace std;
typedef long long ll;
const int MAX_N = 5000, MAX_M = 5000, inf = 1<<30;
int T[MAX_N*MAX_M+1], X[MAX_N*MAX_M+1];
bool P[MAX_N+1][MAX_M+1];
int main()
{
int a, b, c, d, n, m, q;
ll x;
scanf("%lld %d %d %d %d %d %d %d", &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*x%d + b*x%d + c) % d;
swap(T[i], T[x%i + 1]);
}
for (int i = 1; i <= q; ++i) {
int u, v;
scanf("%d %d", &u, &v);
swap(T[u], T[v]);
}
for (int i = 1; i <= n*m; ++i)
X[T[i]] = i;
int x1, x2, y1, y2;
for (int t = 1, r, c; t <= n*m; ++t)
if (!P[r = row(X[t])][c = col(X[t])]) {
printf("%d ", t);
P[r][c] = true;
for (int i = r+1; i <= n && !P[i][c-1]; ++i)
for (int j = c-1; j >= 1 && !P[i][j]; --j)
P[i][j] = true;
for (int i = r-1; i >= 1 && !P[i][c+1]; --i)
for (int j = c+1; j <= m && !P[i][j]; ++j)
P[i][j] = true;
}
return 0;
}