【題解】洛谷P2354 [NOI2014] 隨機數生成器(貪心 模擬)

題面很長 但這道題最大的難度在於貪心和卡常

前面一大堆隨機數怎麼搞出來完全可以直接模擬,注意該long long該取模的地方要做到。然後我們就到了字典序最小的那一部分。這裏我們可以設兩個數組L[x]與R[x],代表第x行最左邊能取第幾列、最右邊能取第幾列。初始化爲1和m。然後我們從小到大枚舉矩陣裏的數,如果這個數所在的列滿足它在所在的行內的L[x]到R[x]區間範圍內,那麼這個數就可以被選擇,然後我們更新L和R數組。因爲只能往右往下走,所以這一行以上的所有行的R[x]變爲min(R[x],這一個數所在的列數),這一行以下的所有行的L[x]變爲max(L[x],這一個數所在的列),然後繼續枚舉下一個數。如果當前的數不符合就跳過。

因爲空間卡的非常死,我們要儘量減少開的數組,首先爲了隨機數生成所開的兩個5000*5000的一位數組(還必須是int類型 long long會炸掉)是不能省的,在尋找某個數所在的位置時,我們可以將模擬已經用過的種子數組X記錄那個數的編號(1,2,3,4,....),然後要用它的位置時在用數學公式計算。具體怎麼操作感覺看代碼就明白了。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=5010;
int x0;
ll a,b,c,d;
int n,m,q;
int t[25000010];
int x[25000010];
int L[maxn],R[maxn];
int findx,findy;
int main()
{
//	freopen("testdata.in","r",stdin);
	scanf("%d%lld%lld%lld%lld",&x0,&a,&b,&c,&d);
	scanf("%d%d%d",&n,&m,&q);
	x[0]=x0;
	for(int i=1;i<=n*m;i++)
	{
		x[i]=(a*x[i-1]%d*x[i-1]%d+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]);
	}
	for(int i=1;i<=q;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		swap(t[x],t[y]);
	}
	for(int i=1;i<=n*m;i++)
	{
		x[t[i]]=i;
	}

	for(int i=1;i<=n;i++)
	{
		L[i]=1;
		R[i]=m;
	}
	for(int i=1;i<=n*m;i++)
	{
		if(x[i]%m!=0) findx=x[i]/m+1;
		else findx=x[i]/m;
		findy=x[i]-(findx-1)*m;
//		cout<<"*****"<<findx<<' '<<findy<<endl;
//		for(int j=1;j<=n;j++)
//		{
//			cout<<L[j]<<' '<<R[j]<<endl;
//		}
//		cout<<"************"<<endl;
		if(L[findx]<=findy&&findy<=R[findx]) 
		{
			for(int j=1;j<=findx-1;j++)
			{
				R[j]=min(R[j],findy);
			}
			for(int j=findx+1;j<=n;j++)
			{
				L[j]=max(L[j],findy);
			}
			cout<<i<<' ';
		}
		else continue;
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章