bzoj 3671 [Noi2014]随机数生成器——贪心(时间复杂度分配)

题目: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;
}

 

posted on 2018-12-17 16:44  Narh  阅读(379)  评论(0编辑  收藏  举报

导航