zoukankan      html  css  js  c++  java
  • ICPC NEAU Programming Contest 2020 [ 随便置换]

    ICPC NEAU Programming Contest 2020 随便置换

    题目大意:

    中文题就自己看吧

    题解:

    这个题目还是很复杂的,挺绕的,想说明白也需要很强的表述能力。

    • 首先假设 (a) 表示原始串,(c) 表示目标串,(p) 表示置换串,那么题目中已知的等式关系是 (a=b*p^m)(p)

    • 第一步先求 (p^m) 这个应该不太难求

    • 直白的来说就是直接把 (p^m) 当作一个置换串 (s) ,已知原串和目标串,求这个中间置换串 (s)

    • 完成第一步之后,这个目标串和原始串就没什么太大的意义了,接下来看这个中间置换串 (s)

    • (s) 也就是 (p^m), 这个是一个变化串,他把 (a) 按照本身的变化规律变成了 (c) 串。

    • 接下来说说 (s) 串的变化规律:

      • (s[1]=6) 表示原来第1个数变成了6

      • (s[2]=4) 表示原来第2个数变成了4,(s[i]) 表示的原来的第 (i) 个变成了第 (s[i])

      • ... 以此类推

    • 已知 (s) 串的变化规律,那么我要求 (s^2) 这个串,那是不是相当于原来的 (s) 串往后挪了两位,(s[1]) 变成了6之后又变成了3,(s[2]) 变成了4之后又变成了7,所以可以得到 (s^2=3745621) 这个串,这个串的变化规律也很好求,就是原来的串往后挪了两位。

    这个图就是上面哪个图两格两格的跳变化出来的图,就是说 (s) 串按照 (s) 串的规律变化一次之后任选一个起点,之后的第 (i) 点是原来的这个点的位置往后跳 (i*2)

    • 接下来讨论(p) 的变化规律,已知 (p^m=p*p...*p) ,所以 (p^m) 是原来 (p) 往后跳 (m) 格形成的图。

    • 此例题的 (m=2) ,先任意选一个起点,假设起点是1:

      • 那么1后面的第 (m) 个格子是6,b[2]=6

        b[0] = 1 b[1]=0 b[2]=6 b[3]=0 b[4]=0 b[5]=0 b[6]=0

      • 那么6后面的第 2个格子是3,所以第4个 b[4]=3

        b[0] = 1 b[1]=0 b[2]=6 b[3]=0 b[4]=3 b[5]=0 b[6]=0

      • 以此类推,最后可以得出这个 (p) 的变化规律

        b[0] = 1 b[1]=4 b[2]=6 b[3]=7 b[4]=3 b[5]=5 b[6]=2

    • 这个变化规律图得到之后,就可以求 (p) 这串是什么了,这个的求法和之前从串变成规律图操作差不多。

      (p[1]=4) (p[4]=6) ...

    以上都是这个题目的整体求解思路,代码中有部分细节讲解。

    #include <bits/stdc++.h>
    using namespace std;
    const int maxn = 2e5+10;
    int a[maxn],f[maxn],cnt[maxn],b[maxn],p[maxn];
    
    int Find(int x){
        return x==f[x]?x:f[x]=Find(f[x]);
    }
    
    void init(int n){
        for(int i=1;i<=n;i++) f[i]=i,cnt[i]=0;
    }
    int gcd(int a,int b){
        return b==0?a:gcd(b,a%b);
    }
    
    int main(){
        int t;
        scanf("%d",&t);
        while(t--){
            int n,m;
            scanf("%d%d",&n,&m);
            init(n);
            for(int i=1,x;i<=n;i++){
                scanf("%d",&x);
                a[x]=i;
                if(Find(i)!=Find(x)){
                    f[i]=x;
                    /*
                     * 这一步再为求环上的节点数量做准备
                     * 也可以用dfs求环上的节点数量
                     * 但是这些都是简单环,所以用并查集比较简单
                     */
                }
            }
            for(int i=1;i<=n;i++) cnt[Find(i)]++;
            /*
             * a就是p^m这个串
             * cnt[i]表示以i点为根节点的这个环的节点数量
             * 接下来求判断是否有解,因为题目可能不止一个环,所以要判断每一个环的大小是不是都是与m互质
             * 如果不互质,那么不可能找到答案,因为不互质,那么总会有一些点找不到
             */
            int flag = 1;
            for(int i=1;i<=n;i++){
                if(!cnt[i]) continue;
                if(gcd(cnt[i],m)!=1){
                    flag = 0;
                    break;
                }
            }
            if(!flag){
                printf("NO
    ");
                continue;
            }
            /*
             * 判断有解之后就开始求p这个串的变化规律了
             */
    //        for(int i=1;i<=n;i++) printf("a[%d]=%d
    ",i,a[i]);
            for(int i=1;i<=n;i++){
                if(!cnt[i]) continue;
                int l = cnt[i], w = i;
                for(int j=0;j<l;j++){
                    b[j*1ll*m%l] = w;
                    w=a[w];
    //                printf("b[%d]=%d
    ",j*m%l,b[j*1ll*m%l]);
                }
                for(int j=0;j<l;j++){
                    p[b[j]]=b[(j+1)%l];
                }
                /*
                 * 这里是在求p串,w首先等于i,表示将i定为起点
                 * 所以b[0]=w
                 * 从这个点往后跳m格b[m]=a[w]
                 * 得到b串之后再转化为p串即可
                 * !!!注意这个位置p[b[j]]=b[(j+1)%l]
                 * 这是因为我存的这个变化串是 a[i] 表示的是原来的第i个变成了第a[i]个
                 * 。。。。好像没有讲清楚,自己思考吧,差不多就是怎么变成图的就怎么变回来
                 * 假设 原来是 x1->x2 a[x2]=x1
                 * 那么之后的 x1->x2  p[x1]=x2
                 */
            }
            printf("YES
    ");
            for(int i=1;i<n;i++) printf("%d ",p[i]);
            printf("%d
    ",p[n]);
        }
    
    }
    
  • 相关阅读:
    内存区间poj 1840
    旋转实现iOS(iPhone/iPad) 屏幕旋转响应函数的缺点与窗口大小位置调整,以及解决办法
    文件模式JAVASE16IO流_3
    配置修改postgresql streaming replication
    查看字段postgresql pg_buffercache
    窗体方法VB.NET设置控件和窗体的显示级别
    流量最小HDU 3491 最小割
    方法返回javascript学习实录 之二(数组操作等等utils) 刘啸尘
    配置路由器静态路由配置
    任务问题Oracle 技术支持之现场优化的思维路径
  • 原文地址:https://www.cnblogs.com/EchoZQN/p/13300262.html
Copyright © 2011-2022 走看看