UVA306 Cipher[循环节]

UVA306 CipherUVA306 Cipher


Descriptionmathcal{Description}

Bob和Alice开始使用一种全新的编码方案。令人惊讶的是,这不是公共关键字密码系统,他们的编码和解码都是基于一种特殊的密码。他们于1996年2月16日在费城的最后一次会议上确定了他们的密码。他们把密码定为一个含有n个不同整数的数列,a1,…,an(0<ai<=n)。解码根据以下原则:把信息写在密钥的下方,使信息中的字符和密钥中的数字相应对齐。在位置i处的消息字符被写入位置ai处,其中ai是密钥中相应的编号。得到的消息以相同的方式继续被编码。重复这个过程k次,第k个编码即是最后答案。

消息的长度总是小于或等于n。若消息长度小于n,用空格补齐。

请你帮助Alice和Bob编写一个读取密钥的程序,进行k次编码,产生编码消息。

N&lt;=10,000N&lt;=10,000


Solutionmathcal{Solution}

最初想法
求出整个置换的循环节长度, 对于后面的每个询问, 都模循环节后暴力进行模拟.
时间复杂度 O(N)O(循环节大小*N),
LuoguLuoguACcolor{green}{AC},
V JudgeV JudgeTLEcolor{blue}{TLE}.


正解部分
由于 整个置换 的循环节大小为 所有 子循环节 大小的 LCMLCM,
导致时间复杂度为 O(LCMN)O(LCM*N), 其中 LCMLCM 的上限为 N!N!, 在luogu竟然还能A.

但若求出每个位置单个的循环节, 对每个询问就可以 O(sizeN)O(size*N) 得到答案, sizesize 的上限为 NN.

求出整个置换的循环节大小需要 2500ms2500ms 左右,
但是求出单个循环节大小仅需要 20ms20ms.


实现部分
没什么好说的.


Codemathcal{Code}

#include<cstdio>
#include<cstring>
#define reg register

const int maxn = 205;

int N;
int K;
int A[maxn];
int B[maxn];
int l[maxn];

char S[maxn];
char Ans[maxn];

void Work(){
        getchar();
        scanf("%[^
]", S+1);
        int len = strlen(S+1);
        while(len < N) S[++ len] = ' ';
        for(reg int i = 1; i <= N; i ++) B[i] = i;
        for(reg int i = 1; i <= N; i ++){
                int tmp = K % l[i];
                while(tmp --) B[i] = A[B[i]];
        }
        for(reg int i = 1; i <= N; i ++) Ans[B[i]] = S[i];
        for(reg int i = 1; i <= N; i ++) printf("%c", Ans[i]);
        printf("
");
}

int main(){
        while(~scanf("%d", &N) && N){
                for(reg int i = 1; i <= N; i ++) scanf("%d", &A[i]);
                for(reg int i = 1; i<= N; i ++){
                        int t = i, to = A[i]; l[i] = 1;
                        while(to != t) l[i] ++, to = A[to];
                }
                while(~scanf("%d", &K) && K) Work();
                printf("
");
        }
        return 0;
}


原文地址:https://www.cnblogs.com/zbr162/p/11822583.html