洛谷P2354 随机数生成器

传送门
题目描述
小 H 最近在研究随机算法。随机算法往往需要通过调用随机数生成函数(例如 Pascal 中的 random 和 C/C++中的 rand)来获得随机性。事实上,随机数生成函数也并不是真正的“随机”,其一般都是利用某个算法计算得来的。

比如,下面这个二次多项式递推算法就是一个常用算法:

算法选定非负整数 x_0,a,b,c,dx
0

,a,b,c,d 作为随机种子,并采用如下递推公式进行计算。

对于任意 i ≥ 1,x_i=(a \times x_{i-1}^2+b \times x_{i-1}+c)\mod di≥1,x
i

=(a×x
i−1
2

+b×x
i−1

+c)modd 这样可以得到一个任意长度的非负整数数列{x_i},i \ge 1{x
i

},i≥1,一般来说,我们认为这个数列是随机的。

利用随机序列 {xi},i≥1xi,i≥1,我们还可以采用如下算法来产生一个 11 到 KK 的随机排列{ Ti },i=1 … k{Ti},i=1…k:

1、初始设 TT 为 11 到 KK 的递增序列;

2、对 TT 进行 KK 次交换,第 ii 次交换,交换 T_iT
i

和 T_{(x_i \mod i) + 1}T
(x
i

modi)+1

的值。

此外,小 H 在这 KK 次交换的基础上,又额外进行了 QQ 次交换操作,对于第i 次额外交换,小 H 会选定两个下标 u_iu
i

和 v_iv
i

,并交换 T_{u_i}T
u
i


和 T_{v_i}T
v
i


的值。

为了检验这个随机排列生成算法的实用性,小 H 设计了如下问题:

小 H 有一个 NN 行 MM 列的棋盘,她首先按照上述过程,通过 N \times M + QN×M+Q 次交换操作,生成了一个 1\sim N \times M1∼N×M 的随机排列 {Ti},i=1 … N \times M{Ti},i=1…N×M,然后将这 N \times MN×M 个数逐行逐列依次填入这个棋盘:也就是第 ii 行第 jj 列的格子上所填入的数应为 T_{(i-1) \times M+j}T
(i−1)×M+j

接着小 H 希望从棋盘的左上角,也就是第一行第一列的格子出发,每次向右走或者向下走,在不走出棋盘的前提下,走到棋盘的右下角,也就是第 NN 行第 MM 列的格子。

小 H 把所经过格子上的数字都记录了下来,并从小到大排序,这样,对于任何一条合法的移动路径,小 H 都可以得到一个长度为 N + M - 1N+M−1 的升序序列,我们称之为路径序列。

小 H 想知道,她可能得到的字典序最小的路径序列应该是怎样的呢?

输入格式
第1行包含5个整数,依次为 x_0,a,b,c,dx
0

,a,b,c,d ,描述小H采用的随机数生成算法所需的随机种子。

第2行包含三个整数 N,M,QN,M,Q ,表示小H希望生成一个1到 N \times MN×M 的排列来填入她 NN 行 MM 列的棋盘,并且小H在初始的 N \times MN×M 次交换操作后,又进行了 QQ 次额外的交换操作。

接下来 QQ 行,第 ii 行包含两个整数 u_i,v_iu
i

,v
i

,表示第 ii 次额外交换操作将交换 T_{u_i}T
u
i


和 T_{v_i}T
v
i


的值。

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

输入输出样例
输入 #1复制
1 3 5 1 71
3 4 3
1 7
9 9
4 9
输出 #1复制
1 2 6 8 9 12
输入 #2复制
654321 209 111 23 70000001
10 10 0
输出 #2复制
1 3 7 10 14 15 16 21 23 30 44 52 55 70 72 88 94 95 97
输入 #3复制
123456 137 701 101 10000007
20 20 0
输出 #3复制
1 10 12 14 16 26 32 38 44 46 61 81 84 101 126 128 135 140 152 156 201 206 237 242 243 253 259 269 278 279 291 298 338 345 347 352 354 383 395
说明/提示
对于样例 1,根据输入的随机种子,小 H 所得到的前 12 个随机数xi为:

9 5 30 11 64 42 36 22 1 9 5 30

根据这 12 个随机数,小 H 在进行初始的 12 次交换操作后得到的排列为:

6 9 1 4 5 11 12 2 7 10 3 8

在进行额外的 3 次交换操作之后,小 H 得到的最终的随机排列为:

12 9 1 7 5 11 6 2 4 10 3 8

12 9 1 7
5 11 6 2
4 10 3 8
最优路径依次经过的数字为 :12-9-1-6-28。

上代码:

#include<bits/stdc++.h>
using namespace std;
const int maxn=5005;
long long a,b,c,d;
int q,m,n,u,v,cnt;
int x[maxn*maxn],t[maxn*maxn];
struct node{
	int l,r;
}e[maxn];
void init(){
	for(int i=1;i<=n*m;++i){
		x[i]=(a*x[i-1]*x[i-1]+b*x[i-1]+c)%d;
		t[i]=i;
	}
	for(int i=1;i<=n*m;++i)
		swap(t[i],t[x[i]%i+1]);
	while(q--){
		scanf("%d%d",&u,&v);
		swap(t[u],t[v]);
	}
	for(int i=1;i<=n*m;++i)
		x[t[i]]=i;
	for(int i=1;i<=n;++i)
		e[i].l=1,e[i].r=m;
}
void work(){
	for(int i=1;i<=n*m;++i){
		int nx,ny;
		if(x[i]%m==0) nx=x[i]/m;
		else nx=x[i]/m+1;
		ny=x[i]%m;
		if(!ny) ny=m;
		if(ny>=e[nx].l&&ny<=e[nx].r){
			++cnt;
			printf("%d ",i);
			if(cnt==n+m-1) return;
			for(int j=1;j<=n;j++){
				if(j<nx) e[j].r=min(e[j].r,ny);
				else if(j>nx) e[j].l=max(e[j].l,ny);
			}	
		}
	}
}
signed main(){
	scanf("%d%lld%lld%lld%lld%d%d%d",&x[0],&a,&b,&c,&d,&n,&m,&q);
	init();
	work();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
洛谷明明的随机数是一个在洛谷(www.luu.com.cn)上的题目,题目编号为P3803。这道题目要求实现一个随机数生成,生成一系列满足特定条件的随机数。 具体来说,题目要求实现一个能够生成区间 [l, r] 内的随机数的函数,并且要求生成的随机数满足以下条件: 1. 生成的随机数在 [l, r] 内均匀分布; 2. 生成的随机数满足给定的概率分布。 在C++中,可以使用rand()函数来生成随机数。rand()函数会生成一个范围在 [0, RAND_MAX] 的随机整数。为了生成 [l, r] 范围内的随机数,可以使用以下公式: random_number = l + rand() % (r - l + 1) 为了满足给定的概率分布,可以使用rand()函数生成一个随机整数,然后根据概率分布进行判断,决定生成的随机数是什么。 下面是一个示例代码,实现了洛谷明明的随机数生成: ```cpp #include <iostream> #include <cstdlib> #include <ctime> using namespace std; int random_number(int l, int r) { return l + rand() % (r - l + 1); } int main() { // 设置随机数种子 srand(time(0)); int l, r; cin >> l >> r; int n; cin >> n; for (int i = 0; i < n; i++) { int num = random_number(l, r); cout << num << " "; } return 0; } ``` 这段代码首先使用srand(time(0))来设置随机数种子,以确保每次运行程序时生成的随机数序列不同。然后通过cin读取输入的范围 [l, r] 和需要生成的随机数个数 n。接下来使用random_number函数生成 n 个满足条件的随机数,并输出到标准输出。 希望以上回答能够帮到你!如果还有其他问题,请继续提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值