[NOI2014]随机数生成器

本文介绍了一种求解字典序最小路径的算法。该算法通过模拟随机生成矩阵,并利用贪心策略从1开始选取最小序列。文章详细阐述了如何通过维护合法区间来判断路径点的有效性及更新矩阵。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意

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

题解

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

复杂度

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值