[2018省队模拟]简单模拟

题目链接

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

思路

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

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

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

性质1.51.5:经过一连串的swap(b1),swap(b2),,swap(bq)swap(b1),swap(b2),⋯,swap(bq)后,11的位置变成了bqbq
证明:由性质11得到。

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

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

  • 按照bb数组的顺序进行两轮交换;
  • 计算出上面性质22中提到的ss值;
  • 利用快速幂,计算出前nq⌊nq⌋项的值;
  • 最后暴力计算出最后nmodqnmodq项的值。

代码

#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;
}

转载于:https://www.cnblogs.com/Canopus-wym/p/10376216.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值