随机数生成器

【问题描述】

小H最近在研究随机算法。随机算法往往需要通过调用随机数生成函数(例如Pascal中的random和C/C++中的rand)来获得随机性。事实上,随机数生成函数也并不是真正的“随机”,其一般都是利用某个算法计算得来的。比如,下面这个二次多项式递推算法就是一个常用算法:算法选定非负整数 x0,a,b,c,d 作为随机种子,并采用如下递推公式进行计算。对于任意 i≥1,这样可以得到一个任意长度的非负整数数列{xi }(i≥1),一般来说,我们认为这个数列是随机的。利用随机序列{xi }(i≥1),我们还可以采用如下算法来产生一个1到K的随机排列{Ti }(i=1)K:初始设T为1到K的递增序列;对T进行K次交换,第 i 次交换,交换 Ti 和 T((x(i) mod i)+1) 的值。此外,小H在这 K 次交换的基础上,又额外进行了 Q 次交换操作,对于第 i 次额外交换,小H会选定两个下标 ui 和 vi,并交换 T(u_i ) 和 T(v_i ) 的值。为了检验这个随机排列生成算法的实用性,小H设计了如下问题:小H有一个 N 行 M 列的棋盘,她首先按照上述过程,通过 N×M+Q 次交换操作,生成了一个 1~N×M 的随机排列 {Ti }(i=1)(N×M),然后将这 N×M 个数逐行逐列依次填入这个棋盘:也就是第 i 行第 j 列的格子上所填入的数应为 T((i-1)●M+j)。接着小H希望从棋盘的左上角,也就是第一行第一列的格子出发,每次向右走或者向下走,在不走出棋盘的前提下,走到棋盘的右下角,也就是第 N 行第 M 列的格子。小H把所经过格子上的数字都记录了下来,并从小到大排序,这样,对于任何一条合法的移动路径,小H都可以得到一个长度为 N+M-1 的升序序列,我们称之为路径序列。小H想知道,她可能得到的字典序最小的路径序列应该是怎样的呢?
【数据规模与约定】
所有测试数据的范围和特点如下表所示:
测试点编号 N,M 的规模 Q 的规模 约定 
1 2≤N,M≤ 8 Q=0 0≤a≤3000≤b,c≤1080≤x0<d≤1081≤ui,vi≤N×M 
2 2≤N,M≤200 

4 2≤N,M≤2000 0≤Q≤50000 


7 2≤N,M≤5000 


10 
【特别提示】
本题的空间限制是 256 MB,请务必保证提交的代码运行时所使用的总内存空间不超过此限制。
一个32位整数(例如C/C++中的int和Pascal中的Longint)为4字节,因而如果在程序中声明一个长度为 1024×1024 的32位整型变量的数组,将会占用 4 MB 的内存空间。

【输入形式】

从文件random.in中读入数据。 输入文件的第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 ) 的值。

【输出形式】

输出到文件random.out中。 输出一行,包含 N+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个随机数x_i为: 
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→6→2→8。 
对于样例3,由于卷面宽度不够,在样例输出中出现了换行。请注意,这里的换行仅作展示用途,事实上,样例输出有且仅有一行,所有的数字都应该出现在同一行中。 
【样例输入输出4】

参见:random.zip

【时间限制】

5s

【空间限制】

256000KB

【上传文件】

上传c, cpp, pas语言源程序,文件名为random.c, random.cpp, random.pas。

题解:

考场上没时间了,写了个暴力找最小+分治都没调出来,不过确实有点慌了。。。

正解是直接从1 到 N*M枚举,如果该点可以被走,那么就一定要走这个点,那么他的左下和右上就不能走,暴力标记为不能走。。。

神做法,好巧妙的说。。。

接下来考虑如何减少暴力的复杂度,有些点可能被删了好多次,因此我们考虑,当我们在标记的时候,碰到了一个点它已经被标记过了,这时候该怎么办

不妨设我们正在标记 x 左下方的点,发现 y  已经被标记过,那么:

1.y 第一次被标记的时候一定是处于某个点的左下方,被打了标记,因为如果是右上方那么 x 应该也被做了标记

2.这样的话处于y左方,下方,以及右下方的一定也被标记过了,我们可以不用标记了,需要做标记的范围可以缩小

实现的话我们可以不用用两个变量来表示需要做标记的边界

可以这样做

1.在内循环里只要碰到标记过的点就break

2.在外循环里如果发现内循环break的时后只循环到了最开始要做标记的边界的时候,外层break

但要注意边界的情况

还有一种做法,是hzwer大神的,用 l,r 数组表示每一行当前没被标记的点的区间,这是因为考虑到每时每刻每一行中可行区域都是连续的,

因此更新的时候,只要维护边界,取min max就行了

代码:

1.考场 0分(我还以为会CE了呢。。。)

 var x,y:array[..] of int64;
a,b,c,d:int64;
num,ans:array[..] of longint;
i,j,n,m,q,t,xx,yy,tot,cnt:longint;
heng,zong:array[..] of longint;
can:array[..] of boolean;
function min(x,y:longint):longint;
begin
if x<y then exit(x) else exit(y);
end;
procedure init;
begin
readln(x[],a,b,c,d);
readln(n,m,q);
for i:= to n*m do x[i]:=((a*((x[i-]*x[i-]) mod d) mod d)+(b*x[i-] mod d+c)) mod d;
for i:= to n*m do y[i]:=i;
for i:= to n*m do
begin
j:=x[i] mod i+;
t:=y[i];y[i]:=y[j];y[j]:=t;
end;
for i:= to q do
begin
readln(xx,yy);
t:=y[xx];y[xx]:=y[yy];y[yy]:=t;
end;
for i:= to n*m do num[y[i]]:=i;
for i:= to n*m do begin heng[i]:=(i-) div m+;zong[i]:=(i-) mod m+;end;
end;
function check(x,y:longint):boolean;
begin
if heng[num[x]]>heng[num[y]] then exit(false);
if zong[num[x]]>zong[num[y]] then exit(false);
exit(true);
end;
function dfs(x1,y1,x2,y2:longint):longint;
var i,j:longint;
begin
dfs:=maxlongint;
for i:=x1 to x2 do
for j:=y1 to y2 do
if ((i=x1) and (j=y1)) or ((i=x2) and (j=y2)) then continue
else dfs:=min(dfs,y[(i-)*m+j]);
end;
procedure update(x1,y1,x2,y2:longint);
var tmp:longint;
begin
if abs(x1-x2)+abs(y1-y2)<= then exit;
tmp:=dfs(x1,y1,x2,y2);if tmp=maxlongint then exit;
can[tmp]:=true;
update(x1,y1,heng[tmp],zong[tmp]);
update(heng[tmp],zong[tmp],x2,y2);
end;
procedure main;
begin
tot:=;
ans[]:=y[];if y[]<> then begin inc(tot);ans[]:=;end;
for i:=tot+ to n+m- do
begin
j:=ans[i-]+;
while not(check(ans[i-],j)) do inc(j);
ans[i]:=j;
if num[j]=n*m then break;
end;
tot:=i;
for i:= to tot do can[ans[i]]:=true;
for i:= to tot- do
update(heng[num[ans[i]]],zong[num[ans[i]]],heng[num[ans[i+]]],zong[num[ans[i+]]]);
cnt:=;
for i:= to n*m do if can[i] then begin inc(cnt);if cnt=n+m- then break;write(i,' ');end;
writeln(i);
end;
begin
assign(input,'random.in');assign(output,'random.out');
reset(input);rewrite(output);
init;
main;
close(input);close(output);
end.

2.筛法没有处理边界 80分

 const maxn=*+;
var x,y:array[..maxn] of longint;
a,b,c,d,x0,x1:int64;
i,j,k,n,m,t,q,xx,yy,cnt:longint;
check:array[..maxn] of boolean;
flag:boolean;
function min(x,y:longint):longint;
begin
if x<y then exit(x) else exit(y);
end;
procedure init;
begin
readln(x0,a,b,c,d);
readln(n,m,q);
for i:= to n*m do
begin
x1:=(x0*(a*x0+b)+c) mod d;
x0:=x1;
x[i]:=x1;
end;
for i:= to n*m do y[i]:=i;
for i:= to n*m do
begin
j:=x[i] mod i+;
t:=y[i];y[i]:=y[j];y[j]:=t;
end;
for i:= to q do
begin
readln(xx,yy);
t:=y[xx];y[xx]:=y[yy];y[yy]:=t;
end;
for i:= to n*m do x[y[i]]:=i;
end;
procedure main;
begin
fillchar(check,sizeof(check),false);
cnt:=;
for i:= to n*m do
begin
if check[i] then continue;
inc(cnt);if cnt<>n+m- then write(i,' ') else begin writeln(i);break;end;
xx:=(x[i]-) div m+;yy:=(x[i]-) mod m+;//writeln(i,' ',x[i],' ',xx,' ',yy);
for j:=xx- downto do
begin
for k:=yy+ to m do
if check[y[(j-)*m+k]] then begin break;end
else check[y[(j-)*m+k]]:=true;
if k=yy+ then break;
end;
for j:=xx+ to n do
begin
for k:=yy- downto do
if check[y[(j-)*m+k]] then begin break;end
else check[y[(j-)*m+k]]:=true;
if k=yy- then break;
end;
end;
//for i:= to n*m do writeln(check[i],' ',y[i]);
end; begin
assign(input,'random.in');assign(output,'random.out');
reset(input);rewrite(output);
init;
main;
close(input);close(output);
end.

3.筛法考虑边界 100分

 const maxn=*+;
var x,y:array[..maxn] of longint;
a,b,c,d,x0,x1:int64;
i,j,k,n,m,t,q,xx,yy,cnt:longint;
check:array[..maxn] of boolean;
flag:boolean;
function min(x,y:longint):longint;
begin
if x<y then exit(x) else exit(y);
end;
procedure init;
begin
readln(x0,a,b,c,d);
readln(n,m,q);
for i:= to n*m do
begin
x1:=(x0*(a*x0+b)+c) mod d;
x0:=x1;
x[i]:=x1;
end;
for i:= to n*m do y[i]:=i;
for i:= to n*m do
begin
j:=x[i] mod i+;
t:=y[i];y[i]:=y[j];y[j]:=t;
end;
for i:= to q do
begin
readln(xx,yy);
t:=y[xx];y[xx]:=y[yy];y[yy]:=t;
end;
for i:= to n*m do x[y[i]]:=i;
end;
procedure main;
begin
fillchar(check,sizeof(check),false);
cnt:=;
for i:= to n*m do
begin
if check[i] then continue;
inc(cnt);if cnt<>n+m- then write(i,' ') else begin writeln(i);break;end;
xx:=(x[i]-) div m+;yy:=(x[i]-) mod m+;//writeln(i,' ',x[i],' ',xx,' ',yy);
for j:=xx- downto do
begin
for k:=yy+ to m do
if check[y[(j-)*m+k]] then begin break;end
else check[y[(j-)*m+k]]:=true;
if (k=yy+) and (k<>m) then break;
end;
for j:=xx+ to n do
begin
for k:=yy- downto do
if check[y[(j-)*m+k]] then begin break;end
else check[y[(j-)*m+k]]:=true;
if (k=yy-) and (k<>) then break;
end;
end;
//for i:= to n*m do writeln(check[i],' ',y[i]);
end; begin
assign(input,'random.in');assign(output,'random.out');
reset(input);rewrite(output);
init;
main;
close(input);close(output);
end.

4.l,r数组维护区间 100分

 const maxn=*+;
var x,y:array[..maxn] of longint;
a,b,c,d,x0,x1:int64;
i,j,k,n,m,t,q,xx,yy,cnt:longint;
l,r:array[..] of longint;
flag:boolean;
function min(x,y:longint):longint;
begin
if x<y then exit(x) else exit(y);
end;
function max(x,y:longint):longint;
begin
if x>y then exit(x) else exit(y);
end;
procedure init;
begin
readln(x0,a,b,c,d);
readln(n,m,q);
for i:= to n*m do
begin
x1:=(x0*(a*x0+b)+c) mod d;
x0:=x1;
x[i]:=x1;
y[i]:=i;
end;
for i:= to n*m do
begin
j:=x[i] mod i+;
t:=y[i];y[i]:=y[j];y[j]:=t;
end;
for i:= to q do
begin
readln(xx,yy);
t:=y[xx];y[xx]:=y[yy];y[yy]:=t;
end;
for i:= to n*m do x[y[i]]:=i;
end;
procedure main;
begin
cnt:=;
for i:= to n do begin l[i]:=;r[i]:=m;end;
for i:= to n*m do
begin
xx:=(x[i]-) div m+;yy:=(x[i]-) mod m+;//writeln(i,' ',x[i],' ',xx,' ',yy);
if (yy<=r[xx]) and (yy>=l[xx]) then
begin
for j:= to n do
if j<xx then r[j]:=min(yy,r[j])
else if j>xx then l[j]:=max(yy,l[j]);
inc(cnt);if cnt<>n+m- then write(i,' ') else begin writeln(i);break;end;
end;
end;
//for i:= to n*m do writeln(check[i],' ',y[i]);
end; begin
assign(input,'random.in');assign(output,'random.out');
reset(input);rewrite(output);
init;
main;
close(input);close(output);
end.

NOI2014 随机数生成器的更多相关文章

  1. [BZOJ3671][UOJ#6][NOI2014]随机数生成器

    [BZOJ3671][UOJ#6][NOI2014]随机数生成器 试题描述 小H最近在研究随机算法.随机算法往往需要通过调用随机数生成函数(例如Pascal中的random和C/C++中的rand)来 ...

  2. 【BZOJ3671】[Noi2014]随机数生成器 暴力

    [BZOJ3535][Noi2014]随机数生成器 Description Input 第1行包含5个整数,依次为 x_0,a,b,c,d ,描述小H采用的随机数生成算法所需的随机种子.第2行包含三个 ...

  3. BZOJ_3671_[Noi2014]随机数生成器_set+贪心

    BZOJ_3671_[Noi2014]随机数生成器_set Description   Input 第1行包含5个整数,依次为 x_0,a,b,c,d ,描述小H采用的随机数生成算法所需的随机种子.第 ...

  4. luogu P2354 [NOI2014]随机数生成器 贪心 卡空间 暴力

    LINK:随机数生成器 观察数据范围还是可以把矩阵给生成出来的. 考虑如何求出答案.题目要求把选出的数字从小到大排序后字典序尽可能的小 实际上这个类似于Mex的问题. 所以要从大到小选数字 考虑选择一 ...

  5. BZOJ3671/UOJ6 [Noi2014]随机数生成器

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000 作者博客:http://www.cnblogs.com/ljh2000-jump/ ...

  6. 贪心 BZOJ 3671:[Noi2014]随机数生成器

    Description   Input 第 1行包含5个整数,依次为 x_0,a,b,c,d ,描述小H采用的随机数生成算法所需的随机种子.第2行包含三个整数 N,M,Q ,表示小H希望生成一个1到 ...

  7. bzoj 3671 [Noi2014]随机数生成器——贪心(时间复杂度分配)

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3671 设 x 为一个点的行号, y 为一个点的列号:原本想着判断一个点能不能选就是看选了的点 ...

  8. bzoj3671 [Noi2014]随机数生成器

    传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3671 [题解] 贪心从1...n*m取,开两个5000*5000的数组就够了,可以重复利用, ...

  9. BZOJ3671 [Noi2014]随机数生成器 【贪心】

    题目链接 BZOJ3671 题解 模拟题意生成矩阵贪心从小选择即可 每选择一个,就标记其左下右上矩阵 由于每次都是标记一个到边界的矩阵,所以一旦遇到标记过就直接退出即可,可以保证复杂度 还有就是空间和 ...

随机推荐

  1. 使用CHttpFile从服务器端正确的读取数据

    前段时间在给软件做升级提示模块的时候发现一个问题,就是使用CHttpFile对象无法从服务器端获取到正确的响应数据长度,无论是使用CHttpFile:: QueryInfo方法,还是使用CHttpFi ...

  2. Headfirst设计模式的C++实现——组合模式(Composite)

    menu_component.h #ifndef _MENU_COMPONENT_H_ #define _MENU_COMPONENT_H_ #include <string> class ...

  3. linux系统使用密钥登录设置

    使用密钥登录linux的操作步骤(使用putty): 1.用putty远程登录linux服务器,然后使用puttygen生成密钥,将生成的密钥保存,保存私钥将公钥复制保存到linux服务器的autho ...

  4. mysql---多表关联

    首先要介绍一下集合的概念:集合具有无序性.唯一性. 无序性:指集合内部元素没有相对顺序的概念,对于两个集合而言,只要元素值和元素个数相同则两个集合相等. 唯一性:指集合内部元素不存在值相等的元素. 上 ...

  5. tomcat错误信息解决方案 严重:StandardServer.await:

    看到这个报错我的第一反应就是端口被占用,用netstat -ant命令查看发现8080端口没有被占用,也可以看到 tomcat的进程已经存在,但是不能对外提供服务. 1.独立运行的tomcat.exe ...

  6. php设计模式-------(1)策略模式

    一.为什么我要学习设计模式. 我的上一个项目是做App接口,由于时间紧,老板催的急,所以到最后项目完工时发现居然写了几万行代码,可想而知代码质量有多糟糕.而且很多时候,调用接口的开发人员来找我说某个接 ...

  7. PHP初学留神(二)

    1.===比较运算符 记得上上篇中说过===与==的问题.当时说,===还要类型相同.但到底是怎样呢?因为我们知道比较运算符是可以把两个值类型转换的.举个栗子,如果一个数字和字符串比较,则字符串会转化 ...

  8. 【7】了解Bootstrap栅格系统基础案例(2)

    ps.这一次要说的是“Responsive column resets”,但是不知道为什么中文官网没有给出翻译,但是在看到案例的时候,感觉这就像一个bug,我自己姑且叫这个是一个高度bug吧,方便自己 ...

  9. CDH安装Hadoop

    一.安装CDH-manager 1.关闭selinux 修改/etc/selinux/config 文件 将SELINUX=enforcing改为SELINUX=disabled 重启机器即可   2 ...

  10. 安装saltstack

    1.安装master 安装epel源 # cd /usr/local/src/ # wget http://mirrors.sohu.com/fedora-epel/6/x86_64/epel-rel ...