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

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

试题描述

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

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

算法选定非负整数x0,a,b,c,d作为随机种子,并采用如下递推公式进行计算。

对于任意i1,xi=(ax2i1+bxi1+c)mod d 。

这样可以得到一个任意长度的非负整数数列{xi}i1,一般来说,我们认为这个数列是随机的。

利用随机序列{xi}i1,我们还可以采用如下算法来产生一个1K随机排列{Ti}i=1~K :

  1. 初始设T1K的递增序列;
  2. T进行K次交换,第i次交换,交换TiT(ximodi)+1的值。

此外,小H在这K次交换的基础上,又额外进行了Q次交换操作,对于第i次额外交换,小H会选定两个下标uivi,并交换TuiTvi的值。

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

小H有一个NM列的棋盘,她首先按照上述过程,通过N×M+Q次交换操作,生成了一个 1N×M的随机排列{Ti}N×Mi=1然后将这N×M个数逐行逐列依次填入这个棋盘:也就是第 i行第 j列的格子上所填入的数应为T(i1)×M+j

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

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

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

输入

输入文件的第1行包含5个整数,依次为x0,a,b,c,d,描述小H采用的随机数生成算法所需的随机种子。

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

接下来Q行,第i行包含两个整数ui,vi,表示第i次额外交换操作将交换 TuiTvi的值

输出

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

输入示例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

输入示例4

传送门(点击下载)

输出示例4

传送门

数据规模及约定

2≤N,M≤5000

0≤Q≤50000

0≤a≤300

0≤b,c≤108

0≤x0<d≤108

1≤ui,vi≤N×M

题解

既然要求排完序之后的字典序最小,那显然可以用贪心来解决。

先把起点和终点的数加进来,之后从1到n·m依次判断。判断数i时先找到i左边最靠近i的一列(称为列L),再找到右边最靠近i的一列(称为列R),以列L中已插入的最靠下的位置和列R中已插入的最靠上的位置分别作为矩形的左上、右下顶点,若i在这个矩形内部则可以取到,否则不能取到。对于每一个可以去到的i,将其行、列的位置加进来。

这个好像可以用线段树搞?

仔细看题,加入操作不超过9999次(n + m - 1次),所以暴!力!即!可!【用线段树反而会多一个log】

下面这个代码是97分的,感谢UOJ用户 r_64 的hack,帮我找到了代码的Bug。(读者不妨看看我错在哪里)

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std;

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *tail;
inline char Getchar() {
    if(Head == tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        tail = (Head = buffer) + l;
    }
    return *Head++;
}
int read() {
    int x = 0, f = 1; char c = Getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
    return x * f;
}

#define maxn 5010
#define maxm 25000010
#define LL long long
int n, m, q, x[maxm], T[maxm], tl[maxn], tr[maxn], high[maxn], low[maxn];

int ans[maxn<<1], cnt;
short idy[maxm];
int main() {
	x[0] = read(); int a = read(), b = read(), c = read(), d = read();
	n = read(); m = read(); q = read();
	for(int i = 1; i <= n * m; i++) {
		x[i] = (((LL)a * x[i-1] * x[i-1]) % d + ((LL)b * x[i-1]) % d + c) % d;
		T[i] = i;
	}
	for(int i = 1; i <= n * m; i++) swap(T[i], T[x[i]%i+1]);
	while(q--) {
		int x = read(), y = read();
		if(x != y) swap(T[x], T[y]);
	}
	for(int i = 1; i <= n * m; i++)
		x[T[i]] = (i-1) / m + 1, idy[T[i]] = (i-1) % m + 1;
	
	for(int i = 2; i < m; i++) tl[i] = 1, tr[i] = m, high[i] = n+1, low[i] = 0;
	high[1] = low[1] = 1; high[m] = low[m] = n;
	for(int i = 1; i <= n * m; i++) if(i != T[1] && i != T[n*m]) {
		int X = x[i], y = idy[i];
		if((y == 1 || low[tl[y]] <= X) && (y == m || X <= high[tr[y]])) {
			ans[++cnt] = i;
			for(int j = tl[y]; j < y; j++) tr[j] = y;
			for(int j = y+1; j <= tr[y]; j++) tl[j] = y;
//			printf("%d %d %d\n", i, x, y);
//			for(int j = 1; j <= m; j++) printf("%d ", tl[j]); putchar('\n');
//			for(int j = 1; j <= m; j++) printf("%d ", tr[j]); putchar('\n');
			low[y] = max(low[y], X); high[y] = min(high[y], X);
		}
	}
	else ans[++cnt] = i;
	
	for(int i = 1; i < cnt; i++) printf("%d ", ans[i]); printf("%d\n", ans[cnt]);
	
	return 0;
}

 100分:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <stack>
#include <vector>
#include <queue>
#include <cstring>
#include <string>
#include <map>
#include <set>
using namespace std;

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *tail;
inline char Getchar() {
    if(Head == tail) {
        int l = fread(buffer, 1, BufferSize, stdin);
        tail = (Head = buffer) + l;
    }
    return *Head++;
}
int read() {
    int x = 0, f = 1; char c = Getchar();
    while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
    while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
    return x * f;
}

#define maxn 5010
#define maxm 25000010
#define LL long long
int n, m, q, x[maxm], T[maxm], tl[maxn], tr[maxn], high[maxn], low[maxn];

int ans[maxn<<1], cnt;
short idy[maxm];
int main() {
	x[0] = read(); int a = read(), b = read(), c = read(), d = read();
	n = read(); m = read(); q = read();
	for(int i = 1; i <= n * m; i++) {
		x[i] = (((LL)a * x[i-1] * x[i-1]) % d + ((LL)b * x[i-1]) % d + c) % d;
		T[i] = i;
	}
	for(int i = 1; i <= n * m; i++) swap(T[i], T[x[i]%i+1]);
	while(q--) {
		int x = read(), y = read();
		if(x != y) swap(T[x], T[y]);
	}
	for(int i = 1; i <= n * m; i++)
		x[T[i]] = (i-1) / m + 1, idy[T[i]] = (i-1) % m + 1;
	
	for(int i = 1; i <= m; i++) tl[i] = 1, tr[i] = m, high[i] = n+1, low[i] = 0;
	high[1] = low[1] = 1; high[m] = low[m] = n;
	for(int i = 1; i <= n * m; i++) if(i != T[1] && i != T[n*m]) {
		int X = x[i], y = idy[i];
		if((y == 1 || low[tl[y]] <= X) && (y == m || X <= high[tr[y]])) {
			ans[++cnt] = i;
			for(int j = tl[y]; j < y; j++) tr[j] = y;
			for(int j = y+1; j <= tr[y]; j++) tl[j] = y;
//			printf("%d %d %d\n", i, x, y);
//			for(int j = 1; j <= m; j++) printf("%d ", tl[j]); putchar('\n');
//			for(int j = 1; j <= m; j++) printf("%d ", tr[j]); putchar('\n');
			low[y] = max(low[y], X); high[y] = min(high[y], X);
		}
	}
	else ans[++cnt] = i;
	
	for(int i = 1; i < cnt; i++) printf("%d ", ans[i]); printf("%d\n", ans[cnt]);
	
	return 0;
}

 

posted @ 2016-04-19 21:15  xjr01  阅读(636)  评论(0编辑  收藏  举报