zoukankan      html  css  js  c++  java
  • ARC110F

    给出一个排列,每次可以swap(p[i],p[(i+p[i])%n])

    要求构造一种方案使得可以还原成递增顺序。

    (nle 100)

    次数限制(2*10^5)


    %%%gmh77当场爆切。

    表示自己想几个晚上都没有想出来,也看不懂别人的题解,然后直接对着代码理解。

    下面尝试模拟正推的思路。

    首先可以发现(1)的特殊性。如果操作(p_i=1)的位置,相当于将这个(1)右移一位。也就是(1)可以在不改变其它的相对顺序的情况下任意穿梭。

    接着考虑如果一直有(p_i eq 0),一直操作(i),到(p_i)还原为止,(p_i)上出现了所有数。

    如果出现了(0)就立即假了,为了排除它的影响,尝试一下将它丢到(p_{n-1})

    对于([1,n-1])的所有数,从大到小,对于(x)将它放到(n-1-x)的位置。每次一直操作(n-1-x)直到达到要求。这样安排的时候(p_{n-1-x}+(n-1-x)<n-1)始终满足,又由于从大到小确定(x)不能在(n-1-x)前面,所以可以构造出来。

    于是得到了个倒序的序列。尝试搞成正序序列:因为(p_x=n-1-x),操作一次之后数字(n-1-x)移动到(p_{n-1})。于是构造在操作(x)的时候,(x)从大往小搞,保证([x+1,n-2])已经是一个升序序列。如此搞:每次将(1)移动到(p_{n-1}),操作一次(x)

    搞完这些之后再操作一次(p_0)即可。


    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 105
    int n;
    int p[N],re[N];
    int ls[1000000],cnt;
    void opt(int i){
    	ls[++cnt]=i;
    	swap(re[p[i]],re[p[(i+p[i])%n]]);
    	swap(p[i],p[(i+p[i])%n]);
    //	for (int k=0;k<n;++k)
    //		printf("%d ",p[k]);
    //	printf("
    ");
    }
    int main(){
    //	freopen("in.txt","r",stdin);
    //	freopen("out.txt","w",stdout);
    	scanf("%d",&n);
    	for (int i=0;i<n;++i)
    		scanf("%d",&p[i]),re[p[i]]=i;
    	while (re[0]!=n-1)
    		opt(re[1]);
    	for (int i=n-1;i>=1;--i)
    		while (re[i]!=n-1-i)
    			opt(n-1-i);
    	for (int i=2;i<n;++i){
    		while (re[1]!=n-1)
    			opt(re[1]);
    		opt(re[i]);
    	}
    	opt(0);
    	printf("%d
    ",cnt);
    	for (int i=1;i<=cnt;++i)
    		printf("%d
    ",ls[i]);
    	return 0;
    }
    
  • 相关阅读:
    Java--Filter(过滤器)
    TP5.1验证Token和Electron-vue头部携带Token
    TP5.1让验证码在另外的项目(Electron-vue)里面使用
    Electron-vue请求携带cookie跨域问题
    Electron-vue在发送请求时携带cookie
    TP5.1解决跨域
    Electron-vue解决跨域
    Electron-vue运行之后出现了文件浏览器
    Electron-vue取消代码检查Eslint
    使用Composer安装TP5.1出现zsh: no matches found: 5.1.*
  • 原文地址:https://www.cnblogs.com/jz-597/p/14152161.html
Copyright © 2011-2022 走看看