题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3671
设 x 为一个点的行号, y 为一个点的列号;原本想着判断一个点能不能选就是看选了的点中 x<cr.x 的 y 的最大值和 x>cr.x 的 y 的最小值,所以想用树状数组维护。
但这样时间空间都会爆。
应该发现“选”一个数最多出现 n+m-1 次!所以考虑 O(1) 判断,在选了一个数之后多花时间维护。
那么可以维护每行能选的列的范围。这个范围一定是一个区间。
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const int N=5005; int n,m,lm,Q; int x[N*N],a[N*N],l[N],r[N]; int rdn() { int ret=0;bool fx=1;char ch=getchar(); while(ch>'9'||ch<'0'){if(ch=='-')fx=0;ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return fx?ret:-ret; } int Mn(int a,int b){return a<b?a:b;} int Mx(int a,int b){return a>b?a:b;} void init() { int a,b,c,d; x[0]=rdn();a=rdn();b=rdn();c=rdn();d=rdn(); n=rdn();m=rdn();Q=rdn(); lm=n*m; for(int i=1;i<=lm;i++) x[i]=((ll)a*x[i-1]*x[i-1]+(ll)b*x[i-1]+c)%d; } int main() { init(); for(int i=1;i<=lm;i++)a[i]=i; for(int i=1;i<=lm;i++)swap(a[i],a[x[i]%i+1]); for(int i=1,u,v;i<=Q;i++) { u=rdn();v=rdn();swap(a[u],a[v]); } for(int i=1;i<=lm;i++)x[a[i]]=i; for(int i=1;i<=n;i++)l[i]=1;for(int i=1;i<=n;i++)r[i]=m; for(int i=1,cnt=0,sm=n+m-1;i<=lm;i++) { int X=(x[i]-1)/m+1,Y=(x[i]-1)%m+1; if(Y>r[X]||Y<l[X])continue; printf("%d ",i);cnt++; if(cnt==lm)break; for(int j=1;j<X;j++)r[j]=Mn(r[j],Y); for(int j=X+1;j<=n;j++)l[j]=Mx(l[j],Y); } puts("");return 0; }