bzoj 3671: [Noi2014]随机数生成器

9 篇文章 0 订阅
3 篇文章 0 订阅

Description

Input

第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 ) 的值。

Output

输出一行,包含 N+M-1 个由空格隔开的正整数,表示可以得到的字典序最小的路径序列。

 

Sample Input

1 3 5 1 71
3 4 3
1 7
9 9
4 9



Sample Output


1 2 6 8 9 12

HINT


本题的空间限制是 256 MB,请务必保证提交的代码运行时所使用的总内存空间不超过此限制。


一个32位整数(例如C/C++中的int和Pascal中的Longint)为4字节,因而如果在程序中声明一个长度为 1024×1024 的32位整型变量的数组,将会占用 4 MB 的内存空间。





 


2≤N,M≤5000


0Q50000


0a300


0≤b,c≤108


0≤x0<d≤108


1≤ui,vi≤N×M




这题因为要求是字典序最小= =所以就可以贪心。一位位取看能不能取到。

取的时候把取的这个数左下右上标记成不可取就行了。

这里放心大单地O(NM)标记。因为每个只会被标记一次所以均摊也就是这个复杂度

【记得找到已经标记过的就直接break】

然后就可以过了

其实还有个难点是数组开不下,要重复利用。标号转换什么的自己推推就好了


#include<cstdio>
#include<string>
#include<cstring>
using namespace std;
int x[25000001];
int t[5001][5001];
int ans[100001];
int main()
{
     long long a,b,c,d;
     scanf("%lld%lld%lld%lld%lld",&x[0],&a,&b,&c,&d);
     int n,m,q;
     scanf("%d%d%d",&n,&m,&q);
     int i,j;
     int nt=n*m;
     for(i=1;i<=nt;i++)
          x[i]=(a*x[i-1]*x[i-1]+b*x[i-1]+c)%d;
     for(i=1;i<=n;i++)
          for(j=1;j<=m;j++)
               t[i][j]=(i-1)*m+j;
     int x1,x2;
     for(i=1;i<=n;i++)
     {
          for(j=1;j<=m;j++)
          {
          	   int loc=x[(i-1)*m+j]%((i-1)*m+j)+1;
          	   x1=(loc-1)/m+1;
               x2=loc-(x1-1)*m;
               int tx=t[i][j];
               t[i][j]=t[x1][x2];
               t[x1][x2]=tx;
          }
     }
     int xx1,yy1,xx2,yy2;
     for(i=1;i<=q;i++)
     {
          scanf("%d%d",&x1,&x2);
          xx1=(x1-1)/m+1;
          yy1=x1-(xx1-1)*m;
          xx2=(x2-1)/m+1;
          yy2=x2-(xx2-1)*m;
          int tx=t[xx1][yy1];
          t[xx1][yy1]=t[xx2][yy2];
          t[xx2][yy2]=tx;
     }
     for(i=1;i<=n;i++)
          for(j=1;j<=m;j++)
               x[t[i][j]]=(i-1)*m+j;
     memset(t,0,sizeof(t));
     int xx=0,yy=0;
     int p=0;
     int ii,jj;
     int ss,tt;
     for(i=1;i<=nt;i++)
     {
          xx=(x[i]-1)/m+1;
          yy=x[i]-(xx-1)*m;
          if(t[xx][yy]!=0)
               continue;
          p++;
          ans[p]=i;
          if(p==n+m-1)
               break;
          ss=1;
          tt=yy-1;
          for(ii=xx+1;ii<=n;ii++)
          {
               if(t[ii][tt]!=0)
                    break;
               for(jj=tt;jj>=ss;jj--)
               {
                    if(t[ii][jj]!=0)
                    {
                         ss=jj+1;
                         break;
                    }
                    t[ii][jj]=1;
               }
               if(ss>tt)
                    break;
          }
          ss=yy+1;
          tt=m;
          for(ii=xx-1;ii>=1;ii--)
          {
               if(t[ii][ss]!=0)
                    break;
               for(jj=ss;jj<=tt;jj++)
               {
                    if(t[ii][jj]!=0)
                    {
                         tt=jj-1;
                         break;
                    }
                    t[ii][jj]=1;
               }
               if(ss>tt)
                    break;
          }
     }
     for(i=1;i<=p-1;i++)
          printf("%d ",ans[i]);
     printf("%d\n",ans[i]);
     return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值