3671: [Noi2014]随机数生成器

本文详细解析了NOI2014随机数生成器问题,介绍了如何通过特定算法生成指定范围内的随机数排列,并利用这些随机数解决实际问题,包括寻找字典序最小的路径序列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

3671: [Noi2014]随机数生成器

Time Limit: 50 Sec  Memory Limit: 256 MB
Submit: 1593  Solved: 704
[ Submit][ Status][ Discuss]

Description

Input

第1行包含5个整数,依次为 x_0,a,b,c,d ,描述小H采用的随机数生成算法所需的随机种子。第2行包含三个整数 N,M,Q ,表示小H希望生成一个1到 N×M 的排列来填入她 N 行 M 列的棋盘,并且小H在初始的 N×M 次交换操作后,又进行了 Q 次额外的交换操作。接下来 Q 行,第 i 行包含两个整数 u_i,v_i,表示第 i 次额外交换操作将交换 T_(u_i )和 T_(v_i ) 的值。

Output

输出一行,包含 N+M-1 个由空格隔开的正整数,表示可以得到的字典序最小的路径序列。

Sample Input

1 3 5 1 71
3 4 3
1 7
9 9
4 9

Sample Output

1 2 6 8 9 12

HINT


本题的空间限制是 256 MB,请务必保证提交的代码运行时所使用的总内存空间不超过此限制。


一个32位整数(例如C/C++中的int和Pascal中的Longint)为4字节,因而如果在程序中声明一个长度为 1024×1024 的32位整型变量的数组,将会占用 4 MB 的内存空间。





2≤N,M≤5000


0≤Q≤50000


0≤a≤300


0≤b,c≤108


0≤x0<d≤1081≤ui,vi≤N×M















Source

[ Submit][ Status][ Discuss]



首先,生成出题目要的那个N*M排列

因为目标是字典序最小的路径序列,所以贪心地想,越小的数字就更需要存在里面了

首先让1进去,然后检查2是否合法,合法就放入否则考虑下一个。。。。。。

因为路径只能向右下方,所以可以产生一些限制

考虑到达每个点所需要的步数是唯一确定的,这就意味着每个点唯一对应路径上的一个位置

每次加入一个点,找到它在路径序列中的位置,找到它的前驱,后继

那么,它和前驱、后继就形成了两个小的矩形,它们之间的点只能在小矩形内产生

所以如果一个点合法,那么O(n)把其它位置的合法区间暴力更新一下就行了

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
 
const int N = 5005;
typedef long long LL;
 
int n,m,q,tot,A[N*N],pos[N*N],u[2*N],l[2*N],d[2*N],r[2*N],Ans[2*N];
LL seed,a,b,c,p;
 
bool cmp(const int &a,const int &b) {return A[a] < A[b];}
int Calc() {return seed = (a * seed % p * seed + b * seed + c) % p;}
 
int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}
 
void Work(int &x,int &y,int &k,int g)
{
    y = g % m; x = g / m;
    k = x + y; ++x; ++y;
}
 
void Modify(int x,int y,int k)
{
    for (int i = k - 1; i >= 0; i--)
    {
        if (Ans[i]) break;
        d[i] = x; r[i] = y;
    }
    for (int i = k + 1; k <= n + m - 1; i++)
    {
        if (Ans[i]) break;
        u[i] = x; l[i] = y;
    }
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    seed = getint(); a = getint(); b = getint(); c = getint();
    p = getint(); n = getint(); m = getint(); q = getint();
    for (int i = 1; i <= n * m; i++)
        A[i] = i,swap(A[i],A[Calc() % i + 1]);
    while (q--)
    {
        int x = getint(),y = getint();
        swap(A[x],A[y]);
    }
    for (int i = 1; i <= n * m; i++) pos[A[i]] = i;
    for (int i = 1; i <= n + m - 1; i++)
        u[i] = 1,d[i] = n,l[i] = 1,r[i] = m;
    for (int i = 1; i <= n * m; i++)
    {
        int x,y,k; Work(x,y,k,pos[i] - 1); if (Ans[k]) continue;
        if (x < u[k] || d[k] < x || y < l[k] || r[k] < y) continue;
        Ans[k] = pos[i]; Modify(x,y,k); ++tot;
        if (tot == n + m - 1) break;
    }
    sort(Ans,Ans + tot,cmp);
    for (int i = 0; i < tot - 1; i++) printf("%d ",A[Ans[i]]);
    cout << A[Ans[tot - 1]] << endl;
    //cerr << (double)(clock()) / CLOCKS_PER_SEC << endl;
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值