[2018省队模拟]简单模拟

本文针对一个特定的数组变换问题,提出了一种高效的解决方案。通过记录每个元素的位置,并利用数组交换的性质,实现了快速计算经过多次交换后的数组状态。具体包括交换操作的特性分析、两轮交换的具体流程以及利用快速幂进行计算。
摘要由CSDN通过智能技术生成

题目链接

http://begin.lydsy.com/JudgeOnline/problem.php?id=5118

思路

题目条件很玄学,要找出值为 bi b i 的元素与 1 1 位置的元素交换。
为了防止到处找元素,我们把每个元素在哪个位置记下来,记录pi为值为 i i 的元素的位置。
这样,题目就变成了:把p数组中的位置 bi b i 的元素与值为 1 1 的元素交换(方便起见,把这样的交换记作swap(bi))。
现在,我们只要关心值为 1 1 的元素的位置。

我们研究研究对p数组交换的性质:

性质 1 1 swap(x)后, 1 1 的位置变成了x
证明:这个结论是很显然的,没有证明。

性质 1.5 1.5 :经过一连串的 swap(b1),swap(b2),,swap(bq) s w a p ( b 1 ) , s w a p ( b 2 ) , ⋯ , s w a p ( b q ) 后, 1 1 的位置变成了bq
证明:由性质 1 1 得到。

性质2:如果经过一轮交换, p p 数组中i位置的值到了 si s i 上,若有 s1=1 s 1 = 1 ,那么现在再次进行同样的交换,这个值会到 ssi s s i 上。
证明:交换的是位置,因此第一次交换后可以把这个数组视为原数组。

根据推论 1.5 1.5 和性质 2 2 ,按照b数组的顺序,进行两轮交换后,可以根据性质 2 2 来快速计算后面的变换情况。
具体流程:

  • 按照b数组的顺序进行两轮交换;

    • 计算出上面性质 2 2 中提到的s值;
    • 利用快速幂,计算出前 nq ⌊ n q ⌋ 项的值;
    • 最后暴力计算出最后 nmodq n mod q 项的值。
    • 代码

      #include <cstdio>
      #include <cstring>
      #include <algorithm>
      
      const int maxn=200000;
      
      int n,q;
      long long m;
      
      struct per
      {
        int a[maxn+10];
      
        per operator *(const per &other) const
        {
          per res;
          for(register int i=1; i<=n; ++i)
            {
              res.a[i]=other.a[a[i]];
            }
          return res;
        }
      };
      
      int a[maxn+10],b[maxn+10],p[maxn+10],opos,tmp[maxn+10];
      per now,res;
      
      int read()
      {
        int x=0,f=1;
        char ch=getchar();
        while((ch<'0')||(ch>'9'))
          {
            if(ch=='-')
              {
                f=-f;
              }
            ch=getchar();
          }
        while((ch>='0')&&(ch<='9'))
          {
            x=x*10+ch-'0';
            ch=getchar();
          }
        return x*f;
      }
      
      int jump(int *ar,int t)
      {
        for(register int i=1; i<=t; ++i)
          {
            std::swap(ar[opos],ar[b[(i-1)%q+1]]);
            opos=b[(i-1)%q+1];
          }
        return 0;
      }
      
      int main()
      {
        n=read();
        q=read();
        scanf("%I64d",&m);
        for(register int i=1; i<=n; ++i)
          {
            a[i]=read();
          }
        for(register int i=1; i<=q; ++i)
          {
            b[i]=read();
          }
        for(register int i=1; i<=n; ++i)
          {
            p[a[i]]=i;
          }
        opos=a[1];
        if(m<=2*q)
          {
            jump(p,m);
            for(register int i=1; i<=n; ++i)
              {
                tmp[p[i]]=i;
              }
            for(register int i=1; i<n; ++i)
              {
                printf("%d ",tmp[i]);
              }
            printf("%d\n",tmp[n]);
            return 0;
          }
        jump(p,q);
        memcpy(tmp,p,sizeof p);
        jump(p,q);
        for(register int i=1; i<=n; ++i)
          {
            now.a[tmp[i]]=p[i];
          }
        for(register int i=1; i<=n; ++i)
          {
            res.a[i]=i;
          }
        long long t=m/q-2;
        while(t)
          {
            if(t&1)
              {
                res=res*now;
              }
            t>>=1;
            now=now*now;
          }
        now=res;
        for(register int i=1; i<=n; ++i)
          {
            p[i]=now.a[p[i]];
          }
        jump(p,m%q);
        for(register int i=1; i<=n; ++i)
          {
            tmp[p[i]]=i;
          }
        for(register int i=1; i<n; ++i)
          {
            printf("%d ",tmp[i]);
          }
        printf("%d\n",tmp[n]);
        return 0;
      }
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值