題面很長 但這道題最大的難度在於貪心和卡常
前面一大堆隨機數怎麼搞出來完全可以直接模擬,注意該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;
}