【bzoj3671】[Noi2014]隨機數生成器 貪心
阿新 • • 發佈:2017-07-06
方法 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]隨機數生成器 貪心