UVA306 Cipher[循环节]

U V A 306   C i p h e r UVA306\ Cipher UVA306 Cipher


D e s c r i p t i o n \mathcal{Description} 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 , 000 N&lt;=10,000 N<=10,000


S o l u t i o n \mathcal{Solution} Solution

最 初 想 法 最初想法
求出整个置换的循环节长度, 对于后面的每个询问, 都模循环节后暴力进行模拟.
时间复杂度 O ( 循 环 节 大 小 ∗ N ) O(循环节大小*N) O(N),
L u o g u Luogu Luogu A C \color{green}{AC} AC,
V   J u d g e V\ Judge V Judge T L E \color{blue}{TLE} TLE.


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

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

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


实 现 部 分 实现部分
没什么好说的.


C o d e \mathcal{Code} 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("%[^\n]", 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("\n");
}

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("\n");
        }
        return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值