1. 程式人生 > >【bzoj3671】[Noi2014]隨機數生成器 貪心

【bzoj3671】[Noi2014]隨機數生成器 貪心

方法 geo light 隨機數生成器 turn ring 表示 交換 復雜

題目描述

技術分享

輸入

第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 ) 的值。

輸出

輸出一行,包含 N+M-1 個由空格隔開的正整數,表示可以得到的字典序最小的路徑序列。

樣例輸入

1 3 5 1 71
3 4 3

1 7
9 9
4 9

樣例輸出

1 2 6 8 9 12


題目大意

給定你一個用亂七八糟的方法生成的n*m的矩陣,矩陣中的元素是1~n*m的全排列,問從左上走到右下的路徑中,把經過的元素從小到大排序後得到的字典序最小的路徑是什麽

題解

貪心

其實我真不知道出題人是怎麽想的,題目描述搞得和數論似的,結果目的就是給出這個序列 = =

顯然要考慮從小到大的n*m個數,如果能選就選,選了就更新其它不能選的位置。

如果一個點被選擇,那麽它的嚴格左下方和嚴格右上方的點都不能被選擇,開一個標記數組記錄它。

如果掃到一個點,它已經被標記過,那麽它的左下(或右上)的點一定都被標記過。適當終止循環,時間復雜度是均攤$O(nm)$的。

但是本題卡內存差評,所以不能開數組記錄每次的x,標記數組必須開成bool的等等。

另外本題需要使用桶排序,否則會TLE。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
int val[25000010] , pos[25000010];
bool tag[5010][5010];
int main()
{
	int a , b , c , d , n , m , k , q , i , jx , jy , px , py , cnt = 0;
	long long x;
	scanf("%lld%d%d%d%d%d%d%d" , &x , &a , &b , &c , &d , &n , &m , &q) , k = n * m;
	for(i = 1 ; i <= k ; i ++ ) val[i] = i , x = (a * x * x + b * x + c) % d , pos[i] = (int)x;
	for(i = 1 ; i <= k ; i ++ ) swap(val[i] , val[pos[i] % i + 1]);
	while(q -- ) scanf("%d%d" , &px , &py) , swap(val[px] , val[py]);
	for(i = 1 ; i <= k ; i ++ ) pos[val[i]] = i;
	for(i = 1 ; i <= k ; i ++ )
	{
		px = (pos[i] - 1) / m + 1 , py = (pos[i] - 1) % m + 1;
		if(!tag[px][py])
		{
			printf("%d%c" , i , ++cnt == n + m - 1 ? ‘\n‘ : ‘ ‘);
			if(py > 1)
			{
				for(jx = px + 1 ; jx <= n ; jx ++ )
				{
					if(tag[jx][py - 1]) break;
					for(jy = py - 1 ; jy >= 1 ; jy -- )
					{
						if(tag[jx][jy]) break;
						tag[jx][jy] = 1;
					}
				}
			}
			if(py < m)
			{
				for(jx = px - 1 ; jx >= 1 ; jx -- )
				{
					if(tag[jx][py + 1]) break;
					for(jy = py + 1 ; jy <= m ; jy ++ )
					{
						if(tag[jx][jy]) break;
						tag[jx][jy] = 1;
					}
				}
			}
		}
	}
	return 0;
}

【bzoj3671】[Noi2014]隨機數生成器 貪心