zoukankan      html  css  js  c++  java
  • 【洛谷P2354】随机数生成器

    题目

    题目链接:https://www.luogu.com.cn/problem/P2354
    小 H 最近在研究随机算法。随机算法往往需要通过调用随机数生成函数(例如 Pascal 中的 random 和 C/C++中的 rand)来获得随机性。事实上,随机数生成函数也并不是真正的“随机”,其一般都是利用某个算法计算得来的。
    比如,下面这个二次多项式递推算法就是一个常用算法:
    算法选定非负整数 \(x_0,a,b,c,d\) 作为随机种子,并采用如下递推公式进行计算。
    对于任意 \(i ≥ 1,x_i=(a \times x_{i-1}^2+b \times x_{i-1}+c)\mod d\) 这样可以得到一个任意长度的非负整数数列\(\{x_i\},i \ge 1\),一般来说,我们认为这个数列是随机的。
    利用随机序列 \({xi},i≥1\),我们还可以采用如下算法来产生一个 \(1\)\(K\) 的随机排列$ { Ti },i=1 ... k$:
    1、初始设 \(T\)\(1\)\(K\) 的递增序列;
    2、对 \(T\) 进行 \(K\) 次交换,第 \(i\) 次交换,交换 \(T_i\)\(T_{x_i \mod i + 1}\) 的值。
    此外,小 H 在这 \(K\) 次交换的基础上,又额外进行了 \(Q\) 次交换操作,对于第i 次额外交换,小 H 会选定两个下标 \(u_i\)\(v_i\),并交换 \(T_{u_i}\)\(T_{v_i}\) 的值。
    为了检验这个随机排列生成算法的实用性,小 H 设计了如下问题:
    小 H 有一个 \(N\)\(M\) 列的棋盘,她首先按照上述过程,通过 \(N \times M + Q\) 次交换操作,生成了一个 \(1\sim N \times M\) 的随机排列 \(\{Ti\},i=1 ... N \times M\),然后将这 \(N \times M\) 个数逐行逐列依次填入这个棋盘:也就是第 \(i\) 行第 \(j\) 列的格子上所填入的数应为 $ T_{(i-1) \times M+u_j} $。
    接着小 H 希望从棋盘的左上角,也就是第一行第一列的格子出发,每次向右走或者向下走,在不走出棋盘的前提下,走到棋盘的右下角,也就是第 \(N\) 行第 \(M\) 列的格子。
    小 H 把所经过格子上的数字都记录了下来,并从小到大排序,这样,对于任何一条合法的移动路径,小 H 都可以得到一个长度为 \(N + M - 1\) 的升序序列,我们称之为路径序列。
    小 H 想知道,她可能得到的字典序最小的路径序列应该是怎样的呢?

    思路

    这道题是真的迷。。。前面叫你做一大堆事情就是无用的模拟。。。
    可能用来卡空间??
    按照题目说的方法可以得出一个\(n\times m\)的矩阵,然后我们需要在矩阵从\((1,1)\)\((n,m)\)的路径中找一条路径,使得这条路径每一个格子的权值排序之后字典序最小。
    由于是排序之后字典序最小,所以我们可以从小到大枚举数字,然后判断它是否能加入路径中。
    显然,一个数字可以加入路径中的必要条件是在这个格子的左下方、右上方均没有格子被选中。这个很好理解。
    那么我们就维护\(L[i]\)表示\(i\sim n\)行所有被选中的格子的最小纵坐标,\(R[i]\)表示\(1\sim i\)行所有被选中格子的最大纵坐标。那么每次判断一个格子是否可以加入是\(O(1)\)的,修改是\(O(n)\)的,但是我们只有\(n+m-1\)个格子会加进路径,且仅有加进路径的格子会修改,所以修改的总复杂度也是\(O(nm)\)(设\(n,m\)同阶)的。
    一开始没有想到只会更新\(n+m-1\)次就写了线段树\(O(\log n)\)修改和查询,这样查询的复杂度是\(O(nm\log m)\)的了。。。
    时间复杂度\(O(nm)\)

    这道题会略卡空间,只能开下\(2\)\(5000*5000\)\(int\)数组,而考虑到每一个点的位置都在\([1,5000]\)内,可以用\(short\)存下,这样空间就不会炸了。

    代码

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    
    const int N=5001;
    int n,m,cnt,Q,t[N*N];
    short pos[N*N][2],R[N],L[N];
    ll x,a,b,c,d;
    
    int main()
    {
    	scanf("%lld%lld%lld%lld%lld",&x,&a,&b,&c,&d);
    	scanf("%d%d%d",&n,&m,&Q);
    	for (register int i=1;i<=n*m;i++)
    		t[i]=i;
    	for (register int i=1;i<=n*m;i++)
    	{
    		x=(x*x*a%d+x*b%d+c)%d;
    		swap(t[i],t[x%i+1]);
    	}
    	for (register int i=1;i<=Q;i++)
    	{
    		int u,v;
    		scanf("%d%d",&u,&v);
    		swap(t[u],t[v]);
    	}
    	for (register int i=1;i<=n;i++)
    		for (register int j=1;j<=m;j++)
    		{
    			pos[t[(i-1)*m+j]][0]=i;
    			pos[t[(i-1)*m+j]][1]=j;
    		}
    	for (int i=1;i<=n;i++)
    		L[i]=m,R[i]=1;
    	for (register int i=1;i<=n*m;i++)
    	{
    		if (pos[i][0]>1 && R[pos[i][0]-1]>pos[i][1]) continue;
    		if (pos[i][0]<n && L[pos[i][0]+1]<pos[i][1]) continue;
    		printf("%d ",i);
    		cnt++;
    		if (cnt==n+m-1) return 0;
    		for (int j=pos[i][0];j<=n;j++) R[j]=max(R[j],pos[i][1]);
    		for (int j=pos[i][0];j>=1;j--) L[j]=min(L[j],pos[i][1]);
    	}
    }
    
  • 相关阅读:
    CentOS 6.2安装Darwin Streaming Server
    流媒体技术笔记(协议相关)
    流媒体技术笔记(视频编码相关)
    CentOS6.2下编译mpeg4ip
    用popen函数操作其它程序的输入和输出
    给centos6.2安装yum源
    启动新进程(fork和exec系列函数实现)
    扩展Asterisk1.8.7的Dialplan Applications
    源码安装ffmpeg(带libx264)
    扩展Asterisk1.8.7的AMI接口
  • 原文地址:https://www.cnblogs.com/stoorz/p/12257530.html
Copyright © 2011-2022 走看看