【无码专区3】乘法表的解密破译

本文探讨了一种特殊的乘法表,基于qqq进制,通过观察字符规律和乘法规则,提出了一种高效的字符数字对应算法。首先识别000和111字符,然后针对小于平方根q的数字,利用全排列和平方特性简化搜索。对于大数字,利用高位重复的特征进行匹配。文章提供了C++代码示例,展示了如何在10s内解决100%的q≤2000问题。
摘要由CSDN通过智能技术生成

因为只有std,没有自我实现,所以是无码专区

problem

给定一个 q q q 进制的乘法表,每个字符代表着 0 ∼ p − 1 0\sim p-1 0p1 不同的数字。

求每个字符代表的数字。保证有解。

× \times ×ABCD
ACDBBDCBA
BBBBBBBBB
CDCBBDBBC
DBABBBCBD

i.e. CD \text{CD} CD 表示的是 p p p 进制下的结果,即: A × A = C ⋅ p + D A\times A=C·p+D A×A=Cp+D

上述例子的答案为: A=3,B=0,C=2,D=1 \text{A=3,B=0,C=2,D=1} A=3,B=0,C=2,D=1

时限: 10 s   256 M B 10s\ 256MB 10s 256MB

我的想法

表示 0 0 0 的字符非常好找:所在“十字架”结构全都是该字符。

表示 1 1 1 的字符也很好找:所在“十字架”结构高位全是 0 0 0 字符,其余字符在低位出现的次数相同。

  • 对于 q≤50 50 % 50\% 50% 部分

显然,所有代表数字 < q <\sqrt{q} <q 的字符,自乘后高位一定是 0 0 0 对应字符,低位字符一定表示一个平方数。

找出所有 < q <\sqrt{q} <q 的字符,全排列枚举每个字符对应的数字,然后搜索乘法表是否冲突。

时间复杂度 O ( q ! q 2 ) O(\sqrt{q}!q^2) O(q !q2)

  • 对于 q≤2000 100 % 100\% 100% 部分

很容易知道在 p p p 进制下,有哪些 ≥ q \ge \sqrt{q} q 的数字平方高位是相同的,即某数字代表字符在高位出现次数是易得的。

如果某些数字出现高位的次数相同,就全排列匹配搜索,否则唯一就可以确定字符和数字的匹配。

时间复杂度未知——只是认为这样可以。


solution

0 0 0 对应字符很好找。

然后对于 1 ∼ q − 1 1\sim q-1 1q1 的每个元素 i i i,其在乘法表中,对应的十位一定是 0 ∼ i − 1 0\sim i-1 0i1,一共是出现了 i i i 个不同的数字。

直接统计十位的个数,然后匹配即可。

时间复杂度 O ( n 2 ) O(n^2) O(n2)

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <queue>
#include <map>

#define N 4005
#define M 15
#define mod 1000000007
#define mod2 100000000
#define ll long long
#define maxi(a,b) (a)>(b)? (a) : (b)
#define mini(a,b) (a)<(b)? (a) : (b)

using namespace std;

int cnt;
int p;
int i, j;
int a[N][N];
int c[N][N];
int cc[N];
int ans[N], pp[N];

int main() {
	freopen("multiplication.in", "r", stdin);
	freopen("multiplication.out", "w", stdout);
	cnt = 1;
	scanf("%d", &p);
	for (i = 0; i < p; i++) {
		for (j = 1; j <= p; j++) {
			scanf("%d", &a[i][2 * j - 1]);
			cc[ a[i][2 * j - 1] ]++;
			scanf("%d", &a[i][2 * j]);
			cc[ a[i][2 * j] ]++;
		}
	}

	int ma = cc[0];
	int index = 0;
	for (i = 0; i < p; i++) {
		if (cc[i] > ma) {
			ma = cc[i];
			index = i;
		}
	}
	ans[0] = index;

	memset(cc, 0, sizeof(cc));

	for (i = 0; i < p; i++) {
		for (j = 1; j <= p; j++) {
			if (c[i][ a[i][2 * j - 1] ] == 0) {
				c[i][ a[i][2 * j - 1] ] = 1;
				cc[i]++;
			}
		}
		if (cc[i] == 1) {
			if (ans[0] != i)
				ans[1] = i;
		} else {
			ans[ cc[i] ] = i;
		}
	}
	for (int i = 0; i < p; i++)
		pp[ans[i]] = i;
	for (i = 0; i < p; i++)
		printf("%d%c", pp[i], " \n"[i == p - 1]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值