[NOI2014]随机数生成器

题意

  给定一个初始随机种子和一个随机数生成器,同时给出若干个交换对,求出交换后的矩阵路径序列排序后字典序最小的合法路径

题解

  一开始的处理很简单,直接按照题目的要求进行模拟,然后将矩阵生成即可,关键是后面的寻找路径过程
  容易想到,1肯定是要被选进去的,因为是要求将路径序列排序后的字典序最小;然后贪心地从小到大选择,判断是否能够加入当前序列,知道序列长度达到要求停止
  现在的问题就是怎么判断决策点能否加入当前序列。考虑对于每一行维护合法的区间(初始为 [1,m] [ 1 , m ] ),设决策点为 (xy) ( x , y ) ,如果 l[x]<=y<=r[x] l [ x ] <= y <= r [ x ] ,那么当前点就可以加入到序列中,然后就要更新矩阵的合法区间
  对于 i[xpre,x] i ∈ [ x p r e , x ] xpre x p r e 为决策点之前那一个点的横坐标),只需将 r[i] r [ i ] 更新为 y y 即可,对于i[x,xnext],只需将 l[i] l [ i ] 更新为 y y <script type="math/tex" id="MathJax-Element-12">y</script>即可

复杂度

O(跑得过)

代码

#include<iostream>
#include<cstdlib>
#include<cstdio>
#define Rint register int
#define Lint long long int
using namespace std;
const int N=5010;
int l[N],r[N],T[N*N],Tn[N*N];
int n,m,q;
Lint a,b,c,d,x_0;
int main()
{
    int u,v,x;
    scanf("%lld%lld%lld%lld%lld",&x_0,&a,&b,&c,&d);
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n*m;i++)   T[i]=i;
    for(int i=1;i<=n*m;i++)
    {
        x=(a*x_0*x_0+b*x_0+c)%d;
        swap( T[i],T[x%i+1] );
        x_0=x;
    }
    for(int i=1;i<=q;i++)
    {
        scanf("%d%d",&u,&v);
        swap( T[u],T[v] );
    }
    for(int i=1;i<=n;i++)   for(int j=1;j<=m;j++)   x=T[(i-1)*m+j],Tn[x]=(i-1)*m+j;
    for(int i=1;i<=n;i++)   l[i]=1,r[i]=m;
    for(int i=1;i<=n*m;i++)
    {
        u=Tn[i]%m ? Tn[i]/m+1:Tn[i]/m,v=Tn[i]%m ? Tn[i]%m:m;
        if( v<l[u] || v>r[u] )   continue ;
        for(int x=u-1;x>=1;x--)
            if( v<r[x] )   r[x]=v;
            else   break ;
        for(int x=u+1;x<=n;x++)
            if( v>l[x] )   l[x]=v;
            else   break ;
        printf("%d ",i);
    }
    printf("\n");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值