2020 CCPC Wannafly Winter Camp Day1 Div.1&2——密码学【构造 & 模拟】

喜欢而已 提交于 2020-01-22 08:02:19

题目传送门


题目描述

考虑一种加密方式,它需要一个任意长度的原文 {m}m 和秘钥 {key}key,其中要求原文和秘钥只包含大写和小写的英文字符。
首先定义字符之间的加密,用字符 aa 去加密字符 bb 的结果是:

  • 首先把 aabb 转成数字 xxyy。转换的规则是,小写字母 aazz 依次对应 002525,大写字母依次对应 26265151
  • 计算 xxyy 的和 zz,对 5252 取模,即计算 (x+y)mod52(x+y) \bmod 52
  • 返回数字 zz 对应的字符。

现在来讲如何用秘钥 keykey 来加密原文 mm

  • 如果秘钥的 keykey 的长度小于 mm,那么不停重复 keykey 直到长度不小于 mm 为止。举例来说,如果原文是 beijingbeijing,秘钥是 PKUSAAPKUSAA,那么秘钥需要被重复称 PKUSAAPKUSAAPKUSAAPKUSAA
  • 假设原文的长度是 nn,那么对于每一个 [1,n][1,n] 的数字 ii,都用 keykey 的第 ii 个字符去加密 mm 的第 ii 个字符。
  • 返回结果。

那么用 PKUSAAPKUSAA 去加密 beijingbeijing 的结果就是:QOcbINVQOcbINV
现在火山哥有 nn 个字符串,s1s_1sns_n,他对这些字符串做了mm 次加密操作:第 ii 次加密操作用第 sxis_{​x_i} 去加密 syis_{​y_i},并把 syis_{​yi} 替换成加密结果。
现在依次给出 mm 次加密操作,以及加密操作结束后每一个字符串的模样,你可以还原出这 nn 个字符串原来的模样吗?


输入描述:

第一行输入两个整数 n,mn,m (1n,m1000)(1n,m1000)(1\leq n,m\leq 1000)(1≤n,m≤1000)
接下来 mm 行每行输入两个整数 xi,yix_i,y_i,表示依次加密操作,保证 xix_i 不等于 yiy_i
接下来 nn 行每行输入一个字符串,表示加密最后的结果。字符串的长度在 11100100 之间,只包含大小写英文字符。


输出描述:

输出 nn 行,每行一个字符串,表示原本的字符串。


输入

2 1
1 2
PKUSAA
QOcbINV


输出

PKUSAA
beijing


题解

  • easy
  • 题面中给出了已知原文和秘钥,加密得到密文的方法。其实已知密文和秘钥,就可以轻松的解密出原文:只要用密文对应的数字减去秘钥对应的数字就可以了。
    因此,我们可以从后往前“撤销”所有加密操作带来的影响。假设最后一次加密操作是用 sxs_x 去加密 sys_y,那么在结果中,sxs_x 是没有发生变化的,即秘钥已知;sys_y,存储的是结果,即密文已知。从而用上面的方法可以直接从两者之中推出 sys_y,原来的值。
  • 所以从后往前考虑每一次加密操作,依次进行撤销,最后得到的就是最开始的 nn 个字符串。

AC-Code

#include <bits/stdc++.h>
using namespace std;

int xi[1005], yi[1005];
string str[1005];
int f1(char c) { return c > 'Z' ? c - 'a' : c - 'A' + 26; }
char f2(int x) { return x >= 26 ? 'A' + x - 26 : 'a' + x; }
void sub(char& s1, char& s2) {
	s2 = f2((f1(s2) - f1(s1) + 52) % 52);
}
void solve(int a, int b) {
	for (int i = 0, j = 0; str[b][j]; ++i, ++j) {
		if (!str[a][i])
			i = 0;
		sub(str[a][i], str[b][j]);
	}
}
int main() {

	int n, m;
	while (cin >> n >> m) {
		for (int i = 0; i < m; ++i) 
			cin >> xi[i] >> yi[i];
		for (int i = 1; i <= n; ++i) 
			cin >> str[i];
		for (int i = m - 1; i >= 0; --i) 
			solve(xi[i], yi[i]);
		for (int i = 1; i <= n; ++i) 
			cout << str[i] << endl;
	}
	return 0;
}

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!