[NOI 2014] 随机数生成器:模拟,贪心

题意:按某种规则生成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 
#include 
#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;
}

你可能感兴趣的:(贪心)