UVa 11476 - Factorizing Larget Integers

Problem

給你一個正整數 $N$ ($1 < N \le10^{18}$),請你把 $N$ 質因數分解。

注意:大整數分解

Sample Input

1
2
3
4
3
60
36
10007

Sample Output

1
2
3
60 = 2^2 * 3 * 5
36 = 2^2 * 3^2
10007 = 10007

Solution

2015/07/11 第二版,加速三倍,加快模乘法運算,減少模數利用減法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
#include <bits/stdc++.h>
using namespace std;
#define MAXL (50000>>5)+1
#define GET(x) (mark[x>>5]>>(x&31)&1)
#define SET(x) (mark[x>>5] |= 1<<(x&31))
int mark[MAXL];
int P[50000], Pt = 0;
void sieve() {
register int i, j, k;
SET(1);
int n = 46340;
for (i = 2; i <= n; i++) {
if (!GET(i)) {
for (k = n/i, j = i*k; k >= i; k--, j -= i)
SET(j);
P[Pt++] = i;
}
}
}
long long mul(unsigned long long a, unsigned long long b, unsigned long long mod) {
long long ret = 0;
for (a %= mod, b %= mod; b != 0; b >>= 1, a <<= 1, a = a >= mod ? a - mod : a) {
if (b&1) {
ret += a;
if (ret >= mod) ret -= mod;
}
}
return ret;
}
void exgcd(long long x, long long y, long long &g, long long &a, long long &b) {
if (y == 0)
g = x, a = 1, b = 0;
else
exgcd(y, x%y, g, b, a), b -= (x/y) * a;
}
long long llgcd(long long x, long long y) {
if (x < 0) x = -x;
if (y < 0) y = -y;
if (!x || !y) return x + y;
long long t;
while (x%y)
t = x, x = y, y = t%y;
return y;
}
long long inverse(long long x, long long p) {
long long g, b, r;
exgcd(x, p, g, r, b);
if (g < 0) r = -r;
return (r%p + p)%p;
}
long long mpow(long long x, long long y, long long mod) { // mod < 2^32
long long ret = 1;
while (y) {
if (y&1)
ret = (ret * x)%mod;
y >>= 1, x = (x * x)%mod;
}
return ret % mod;
}
long long mpow2(long long x, long long y, long long mod) {
long long ret = 1;
while (y) {
if (y&1)
ret = mul(ret, x, mod);
y >>= 1, x = mul(x, x, mod);
}
return ret % mod;
}
int isPrime(long long p) { // implements by miller-babin
if (p < 2 || !(p&1)) return 0;
if (p == 2) return 1;
long long q = p-1, a, t;
int k = 0, b = 0;
while (!(q&1)) q >>= 1, k++;
for (int it = 0; it < 2; it++) {
a = rand()%(p-4) + 2;
t = mpow2(a, q, p);
b = (t == 1) || (t == p-1);
for (int i = 1; i < k && !b; i++) {
t = mul(t, t, p);
if (t == p-1)
b = 1;
}
if (b == 0)
return 0;
}
return 1;
}
long long pollard_rho(long long n, long long c) {
long long x = 2, y = 2, i = 1, k = 2, d;
while (true) {
x = (mul(x, x, n) + c);
if (x >= n) x -= n;
d = llgcd(x - y, n);
if (d > 1) return d;
if (++i == k) y = x, k <<= 1;
}
return n;
}
void factorize(int n, vector<long long> &f) {
for (int i = 0; i < Pt && P[i]*P[i] <= n; i++) {
if (n%P[i] == 0) {
while (n%P[i] == 0)
f.push_back(P[i]), n /= P[i];
}
}
if (n != 1) f.push_back(n);
}
void llfactorize(long long n, vector<long long> &f) {
if (n == 1)
return ;
if (n < 1e+9) {
factorize(n, f);
return ;
}
if (isPrime(n)) {
f.push_back(n);
return ;
}
long long d = n;
for (int i = 2; d == n; i++)
d = pollard_rho(n, i);
llfactorize(d, f);
llfactorize(n/d, f);
}
int main() {
sieve();
int testcase;
scanf("%d", &testcase);
while (testcase--) {
long long n;
scanf("%lld", &n);
vector<long long> f;
map<long long, int> r;
llfactorize(n, f);
for (auto &x : f)
r[x]++;
printf("%lld =", n);
for (auto it = r.begin(); it != r.end(); it++) {
if (it != r.begin())
printf(" *");
printf(" %lld", it->first);
if (it->second > 1)
printf("^%d", it->second);
}
puts("");
}
return 0;
}
Read More +

b432. 趣味加分

Problem

背景

廖氏如神 (pcsh710742) 在作業中遇到一題,用手算會算到天昏地暗,而問題是這樣子的:

$a^{13337} \equiv n \mod 2^{64}$

其中 $n= 2015 + 2 \times (\text{The last 2 digit of your student ID number})$,請找到 $a < 2^{64}$ 的其中一組解。

問題描述

模數 $2^{64}$ 看起來不大,但對於 Ghz 為 CPU 運算速度單位的電腦而言還是要跑非常久的。因此將問題簡化:

$a^{23333} \equiv n \mod 2^{20}$

現在給予一個 $n$,請求出一組 $a$,測資中保證答案唯一。

Sample Input

1
2
3
4
5
268275
888817
89215
63495
976477

Sample Output

1
2
3
4
5
387
817
639
487
909

Solution

除了偶數無解外,奇數都至少有一個解。而這一題的題目數據恰好奇數都只有一解,那麼就不必處理多組解或者是字典順序最小的,只要專心找到符合的解即可。

  • 暴力建表 $O(2^{20})$ 建完,之後直接查找。
  • 篩選正解,依序窮舉從最低位到最高位 (二進制下) 為 0 還是 1,由於次方會不斷地推移,高位結果不影響低位的對應。窮舉時保留低位符合的解,並且不斷地篩選掉不可能的解方案,複雜度 $O(20 k)$$k$ 是難以估計的數字。
  • 快速假解,類似篩選正解的做法,但只保留其中一組解進行,複雜度 $O(20)$。這個解法是有點毛病的,但目前找不到反例。

快速假解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <bits/stdc++.h>
using namespace std;
long long mul(long long a, long long b, long long mod) {
long long ret = 0;
for (a %= mod, b %= mod; b != 0; b>>=1, a <<= 1, a = a >= mod ? a - mod : a) {
if (b&1) {
ret += a;
if (ret >= mod) ret -= mod;
}
}
return ret;
}
unsigned long long mpow(unsigned long long x, unsigned long long y, unsigned long long mod) {
unsigned long long ret = 1;
while (y) {
if (y&1)
ret = (ret * x)%mod;
y >>= 1, x = (x * x)%mod;
}
return ret % mod;
}
// find a^23333 = n \mod 2^32
int main() {
const long long M = 1LL<<20;
const long long E = 23333;
long long n;
while (scanf("%lld", &n) == 1) {
long long a = 0;
for (int i = 0; i < 32; i++) {
long long t = mpow(a, E, M), mask = (1LL<<(i+1))-1;
if ((t&mask) == (n&mask)) {
} else {
a |= 1LL<<i;
}
}
printf("%lld\n", a);
}
return 0;
}

篩選正解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <bits/stdc++.h>
using namespace std;
unsigned long long mpow(unsigned long long x, unsigned long long y, unsigned long long mod) {
unsigned long long ret = 1;
while (y) {
if (y&1)
ret = (ret * x)%mod;
y >>= 1, x = (x * x)%mod;
}
return ret % mod;
}
// find a^23333 = n \mod 2^32
int main() {
const long long M = 1LL<<20;
const long long E = 23333;
long long n;
while (scanf("%lld", &n) == 1) {
vector<long long> A(1, 0);
for (int i = 0; i < 20; i++) {
vector<long long> nA;
for (auto &a : A) {
long long t, mask;
t = mpow(a, E, M), mask = (1LL<<(i+1))-1;
if ((t&mask) == (n&mask)) {
nA.push_back(a);
}
t = mpow(a|(1LL<<i), E, M), mask = (1LL<<(i+1))-1;
if ((t&mask) == (n&mask)) {
nA.push_back(a|(1LL<<i));
}
}
A = nA;
}
assert(A.size() > 0 && mpow(A[0], E, M) == n);
printf("%lld\n", A[0]);
}
return 0;
}
Read More +

b431. 中國餘數

Problem

背景

中國餘數定理 (Chinese Remainder Theorem,簡稱 CRT) 經常是工程學裡面常用的一種轉換域,很多人不知道當初在大學離散數學中學這個做什麼,但是在不少計算的設計都會運用到 CRT。由於電腦 CPU 架構中的運算單位是 32-bits 或者 64-bits (也許在未來會更長),但值域高達 128-bits 或者 512-bits 以上模擬運算成了麻煩之處。

回顧中國餘數定理 CRT

$$(S): \left\{\begin{matrix} x \equiv a_1 \mod m_1 \\ x \equiv a_2 \mod m_2 \\ \vdots \\ x \equiv a_n \mod m_n \end{matrix}\right.$$
  • $m_1, m_2, \cdots , m_n$ 任兩數互質,意即 $\forall i \neq j, gcd(m_i, m_j) = 1$
  • 對於任意整數 $a_1, a_2, \cdots , a_n$ 方程組 $(S)$ 均有解,意即找得到一個 $x$ 的參數解。

構造法解 CRT

  1. $M = m_1 \times m_2 \times \cdots \times m_n = \prod_{i=1}^{n} m_i$
  2. $M_i = M / m_i$
  3. $t_i = M_i^{-1} \mod m_i<span>$,意即$</span><!-- Has MathJax -->t_i M_i \equiv 1 \mod m_i$
  4. 方程組 $(S)$ 的通解形式為: $x = a_1 t_1 M_1 + a_2 t_2 M_2 + \cdots + a_n t_n M_n + kM = kM + \sum_{i = 1}^{n} a_i t_i M_i, k \in \mathbb{Z}$
  5. 若限定 $0 \le x < M$,則 $x$ 只有一解。

很多人會納悶通解為什麼長那樣,原因很簡單,要滿足方程組每一條式子,勢必對於$a_i t_i M_i$ 要滿足$x \equiv a_i \mod m_i$ ,因此 $a_i t_i M_i \equiv a_i (t_i M_i) \mod m_i \equiv a_i \mod m_i$ 成立,但是$\forall i \neq j$,滿足$a_i t_i M_i \equiv a_i (t_i M_i) \mod m_j \equiv 0 \mod m_j$

問題描述

來個簡單運用,來計算簡單的 RSA 加解密,特化其中的數學運算。

$M \equiv C^d \mod n$ $n = p \times q$,其中 $p, q$ 是兩個不同的質數,已知 $C, d, p, q$,請求出 $M$

## Sample Input ##
1
2
88 7 17 11
11 23 17 11

Sample Output

1
2
11
88

Solution

RSA 可以預先處理

  • $c_p = q \times (q^{-1} \mod p)$
  • $c_q = p \times (p^{-1} \mod q)$

還原的算法則是 $M = Mp \times c_p + Mq \times c_q \mod N$

由於拆分後的 bit length 少一半,乘法速度快 4 倍,快速冪次方快 2 倍 (次方的 bit length 少一半),但是要算 2 次,最後共計快 4 倍。CPU 的乘法想必不會用快速傅立葉 FFT 來達到乘法速度為 $O(n \log n)$

特別小心「 bit length 少一半 」必須在 $gcd(C, p) = 1$ 時才成立,互質機率機率非常高,仍然有不成立的時候,這情況下速度不是加快 400%。請參照一般解。

例如 C = 27522, d = 17132, p = 2, q = 17293,若使用歐拉定理計算 $Mp = C^{d \mod (p-1)} \mod p = 1$ 事實上 $Mp = C^d \mod p = 0$。有一個特性尚未被利用,對於模質數 $p$ 而言,所有數 $x$ 在模 $p$ 下的循環長度 $L | (p-1)$,最後可以套用 $Mp = C^{d \mod (p-1) + (p-1)} \mod p$ 來完成。請參照循環解,如此一來就不必先判斷 $gcd(C, p) = 1$

番外

但是利用 CRT 計算容易受到硬體上攻擊,因為會造成 $p, q$ 在分解過程中獨立出現,當初利用 $N$ 很難被分解的特性來達到資訊安全,但是卻因為加速把 $p, q$ 存放道不同時刻的暫存器中。

其中一種攻擊,計算得到 $M = Mp \times q \times (q^{-1} \mod p) + Mq \times p \times (p^{-1} \mod q) \mod N$ 當擾亂後面的式子 (提供不穩定的計算結果)。得到 $M' = Mp \times q \times (q^{-1} \mod p) + (Mq + \Delta) \times p \times (p^{-1} \mod q) \mod N$

接著 $(M' - M) = (\Delta' \times p) \mod N$,若要求 $p$ 的方法為 $gcd(M' - M, N) = gcd(\Delta' \times p, N) = p$,輾轉相除的概念呼之欲出,原來 $p$ 會被這麼夾出,當得到兩個 $p, q$,RSA 算法就會被破解。

一般解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include <bits/stdc++.h>
using namespace std;
long long mul(long long a, long long b, long long mod) {
long long ret = 0;
for (a %= mod, b %= mod; b != 0; b>>=1, a <<= 1, a = a >= mod ? a - mod : a) {
if (b&1) {
ret += a;
if (ret >= mod) ret -= mod;
}
}
return ret;
}
void exgcd(long long x, long long y, long long &g, long long &a, long long &b) {
if (y == 0)
g = x, a = 1, b = 0;
else
exgcd(y, x%y, g, b, a), b -= (x/y) * a;
}
long long llgcd(long long x, long long y) {
long long t;
while (x%y)
t = x, x = y, y = t%y;
return y;
}
long long inverse(long long x, long long p) {
long long g, b, r;
exgcd(x, p, g, r, b);
if (g < 0) r = -r;
return (r%p + p)%p;
}
long long mpow(long long x, long long y, long long mod) {
long long ret = 1;
while (y) {
if (y&1)
ret = (ret * x)%mod;
y >>= 1, x = (x * x)%mod;
}
return ret % mod;
}
int main() {
long long C, d, p, q;
long long N, M, Cp, Cq, Mp, Mq;
while (scanf("%lld %lld %lld %lld", &C, &d, &p, &q) == 4) {
N = p * q;
Mp = mpow(C%p, llgcd(C, p) == 1 ? d%(p-1) : d, p);
Mq = mpow(C%q, llgcd(C, q) == 1 ? d%(q-1) : d, q);
Cp = q*inverse(q, p)%N;
Cq = p*inverse(p, q)%N;
M = (mul(Mp, Cp, N) + mul(Mq, Cq, N))%N;
printf("%lld\n", M);
}
return 0;
}

循環解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long UINT64;
UINT64 mul(UINT64 a, UINT64 b, UINT64 mod) {
UINT64 ret = 0;
for (a = a >= mod ? a%mod : a, b = b >= mod ? b%mod : b; b != 0; b>>=1, a <<= 1, a = a >= mod ? a - mod : a) {
if (b&1) {
ret += a;
if (ret >= mod)
ret -= mod;
}
}
return ret;
}
void exgcd(long long x, long long y, long long &g, long long &a, long long &b) {
if (y == 0)
g = x, a = 1, b = 0;
else
exgcd(y, x%y, g, b, a), b -= (x/y) * a;
}
long long inverse(long long x, long long p) {
long long g, b, r;
exgcd(x, p, g, r, b);
if (g < 0) r = -r;
return (r%p + p)%p;
}
long long mpow(long long x, long long y, long long mod) {
long long ret = 1;
while (y) {
if (y&1)
ret = (ret * x)%mod;
y >>= 1, x = (x * x)%mod;
}
return ret % mod;
}
int main() {
long long C, d, p, q;
long long N, M, Cp, Cq, Mp, Mq;
while (scanf("%lld %lld %lld %lld", &C, &d, &p, &q) == 4) {
N = p * q;
Mp = mpow(C%p, d%(p-1) + (p-1), p);
Mq = mpow(C%q, d%(q-1) + (q-1), q);
Cp = mul(q, inverse(q, p), N);
Cq = mul(p, inverse(p, q), N);
M = (mul(Mp, Cp, N) + mul(Mq, Cq, N))%N;
printf("%lld\n", M);
}
return 0;
}
Read More +

b430. 簡單乘法

Problem

背景

在早期密碼世界中, 各種運算都先講求速度,不管是在硬體、軟體利用各種數學定義來設計加密算法就為了加快幾倍的速度,但在近代加密中,加速方法會造成硬體實作攻擊,速度和安全,你選擇哪一個呢。

題目描述

$$a b \equiv x \mod n$$

已知 $a, b, n$,求出 $x$

Sample Input

1
2
3
4
3 5 7
2 4 3
2 0 2
5 1 4

Sample Output

1
2
3
4
1
2
0
1

Solution

利用加法代替乘法避免溢位,加法取模換成減法加快速度。

由於這一題範圍在在 $10^{18}$,用 long long 型態沒有問題,若在 $2^{63}$ 請替換成 unsigned long long

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <bits/stdc++.h>
using namespace std;
long long mul(long long a, long long b, long long mod) {
long long ret = 0;
for (a %= mod, b %= mod; b != 0; b>>=1, a <<= 1, a = a >= mod ? a - mod : a) {
if (b&1) {
ret += a;
if (ret >= mod)
ret -= mod;
}
}
return ret;
}
int main() {
long long a, b, n;
while (scanf("%lld %lld %lld", &a, &b, &n) == 3)
printf("%lld\n", mul(a, b, n));
return 0;
}
Read More +

b429. 離散對數

Problem

背景

高中時上過對數,了解 $a^x = b$,則 $x = \log_{a}{b}$。這個問題很簡單,但是 log() 又是怎麼運作,當時是用查表法,不久在大學就會學到泰勒級數,藉由電腦運算,計算越多次就能更逼近真正的結果。

離散對數的形式如下:

$$a^x \equiv b \mod n$$

已知 $a, b, n$,通常會設定 $0 \le a, b < n$。這問題的難處在於要怎麼解出 $x$,沒有 log() 可以這麼迅速得知。

為什麼需要離散對數?不少的近代加密算法的安全強度由這個問題的難度決定,例如 RSA 加密、Diffie-Hellman 金鑰交換 … 等,實際運用需要套用許多數論原理。然而,加密機制要保證解得回來,通常會保證 $gcd(a, n) = 1$,讓乘法反元素 (逆元) 存在。

問題描述

$$a^x \equiv b \mod n$$

已知 $a, b, n$,解出最小的 $x$,若不存在解則輸出 NOT FOUND

Sample Input

1
2
3
4
2 1 5
2 2 5
3 5 17
4 2 17

Sample Output

1
2
3
4
0
1
5
NOT FOUND

Solution

解決問題 $y = g^x \mod p$,當已知 $y, g, p$,要解出 $x$ 的難度大為提升,不像國高中學的指數計算,可以藉由 log() 運算來完成,離散對數可以的複雜度相當高,當 $p$ 是一個相當大的整數時,通常會取用到 256 bits 以上,複雜度則會在 $O(2^{100})$ 以上。

實際上有一個有趣的算法 body-step, giant-step algorithm,中文翻譯為 小步大步算法 ,在 ACM-ICPC 競賽中也可以找到一些題目來玩玩,算法的時間複雜度為 $O(\sqrt{p})$,空間複雜度也是 $O(\sqrt{p})$。相信除了這個外,還有更好的算法可以完成。

小步大步算法其實很類似塊狀表的算法,分塊處理,每一塊的大小為 $\sqrt{p}$,為了找尋答案計算其中一塊的所有資訊,每一塊就是一小步,接著就是利用數學運算,拉動數線,把這一塊往前推動 (或者反過來把目標搜尋結果相對往塊的地方推動)。因此需要走 $\sqrt{p}$ 大步完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <bits/stdc++.h>
using namespace std;
// Baby-step Giant-step Algorithm
// a x + by = g
void exgcd(long long x, long long y, long long &g, long long &a, long long &b) {
if (y == 0)
g = x, a = 1, b = 0;
else
exgcd(y, x%y, g, b, a), b -= (x/y) * a;
}
long long inverse(long long x, long long p) {
long long g, b, r;
exgcd(x, p, g, r, b);
if (g < 0) r = -r;
return (r%p + p)%p;
}
long long BSGS(long long P, long long B, long long N) {
unordered_map<long long, int> R;
long long sq = (long long) sqrt(P);
long long t = 1, f;
for (int i = 0; i < sq; i++) {
if (t == N)
return i;
if (!R.count(t))
R[t] = i;
t = (t * B) % P;
}
f = inverse(t, P);
for (int i = 0; i <= sq+1; i++) {
if (R.count(N))
return i * sq + R[N];
N = (N * f) % P;
}
return -1;
}
int main() {
long long P, B, N; // find B^L = N mod P
while (scanf("%lld %lld %lld", &B, &N, &P) == 3) {
long long L = BSGS(P, B, N);
if (L == -1)
puts("NOT FOUND");
else
printf("%lld\n", L);
}
return 0;
}
Read More +

b433. 中間相遇法

Problem

背景

加密的每一道過程若存在反運算,則存在解密的程序。絕對安全的加密沒有反運算,那解密也是沒有辦法做到的。通常加解密中一定會用到互斥或 (XOR) 運算,從表格來看,單獨看任何一行一列都恰好分配一半的 0、一半的 1。對於密碼來說,明文跟密文的關係,不管知道的明文還是密文的部分,猜中的機率只有 $1/2$,只要位元數一多,猜中的機率近乎 $(\frac{1}{2})^n \cong 0$

「只用 XOR key 是不行的,再做一次不就回來了嗎?」

「那用循環位移和加法的 overflow 破壞線性結構!」

「但記得要能解密回來哦。」

於是某 M 提供一個簡單的加密運算,明文 $M$,加密金鑰 $key$

  • 密文 $C = ((M \text{<<<} key) + key) \text{ XOR } key$
  • 明文 $M = ((C \text{ XOR } key) + (\sim key) + 1) \text{>>>} key$

其中每一個運算都在 16-bit CPU 上運作,$\text{<<<}$ 表示循環左移 (circular shift),$\sim$ 表示 1 補數。

用抽象化表示加解密流程

  • $C = E_{key}(M)$
  • $M = D_{key}(C)$

問題描述

現在某 M 正用他自己設計的加解密協定跟未來的自己溝通,但發現到這種加解密,如果對方知道某 M 一定會傳送哪一個明文,接著只要匹配密文,窮舉 $O(2^{16})$ 來找到加密金鑰就會破功。

在電影《模仿遊戲》中,德國納粹 Enigma 密碼機,訊息中的結尾一定會附加「希特勒萬歲!(Heil Hitler!)」匹配密文後,窮舉金鑰就能破解。而某 M 常常傳送「萌萌哒!(Moe Moe Ta!)」只需要 $O(2^{16})$ 就能被破解。

於是某 M 強化他的加密協定,希望破解者至少要在 $O(2^{32})$ 的時間內找到,拖延破解時間。

$C = E_{key_2}(E_{key_1}(M))$ $M = D_{key_1}(D_{key_2}(C))$

現在知道某 M 傳送的其中一組 $(C, M)$,請告訴某 M 有多少組 $(key_1, key_2)$ 的可能性。萬萬沒想到,中間相遇法能在 $O(2^{16} \times 16)$ 解決這個問題。

Sample Input

1
2
40380 23333
30767 13657

Sample Output

1
2
114688
45568

Solution

套用中間相遇法$E_{key_1}(M) = D_{key_2}(C)$ 分別對等式兩邊進行計算,查看相碰情況。

複雜度為窮舉所有可能的 $key_1$$key_2$,若用平衡樹進行碰撞檢查 $O(2^{16} \times 16)$,利用 HASH 可以降到 $O(2^{16})$,由於 $O(2^{16})$ 是記憶體可以宣告的,則直接使用數組來完成。

Hash

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <bits/stdc++.h>
using namespace std;
typedef unsigned short int UINT16;
class SimpleIdea {
public:
UINT16 key;
SimpleIdea(UINT16 k = 0):
key(k) {}
UINT16 encrypt(UINT16 m) {
return (rotate_left(m, key&15) + key)^key;
}
UINT16 decrypt(UINT16 m) {
return rotate_left((m^key)+(~key)+1, 16 - (key&15));
}
private:
UINT16 rotate_left(UINT16 x, UINT16 n) {
return (x << n) | (x >> (16-n));
}
} test;
int main() {
int C, M;
while (scanf("%d %d", &C, &M) == 2) {
unordered_map<int, int> H1, H2;
for (int i = 0; i < (1<<16); i++) {
int key1 = i, key2 = i;
test.key = key1;
H1[test.encrypt(M)]++;
test.key = key2;
H2[test.decrypt(C)]++;
}
int ret = 0;
for (auto &x : H1) {
if (H2.count(x.first))
ret += H2[x.first] * x.second;
}
printf("%d\n", ret);
}
return 0;
}

Array

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <bits/stdc++.h>
using namespace std;
typedef unsigned short int UINT16;
class SimpleIdea {
public:
UINT16 key;
SimpleIdea(UINT16 k = 0):
key(k) {}
UINT16 encrypt(UINT16 m) {
return (rotate_left(m, key&15) + key)^key;
}
UINT16 decrypt(UINT16 m) {
return rotate_left((m^key)+(~key)+1, 16 - (key&15));
}
private:
UINT16 rotate_left(UINT16 x, UINT16 n) {
return (x << n) | (x >> (16-n));
}
} test;
int main() {
int C, M;
while (scanf("%d %d", &C, &M) == 2) {
int H[1<<16] = {}, ret = 0;
for (int i = 0; i < (1<<16); i++) {
test.key = i;
H[test.encrypt(M)]++;
}
for (int i = 0; i < (1<<16); i++) {
test.key = i;
ret += H[test.decrypt(C)];
}
printf("%d\n", ret);
}
return 0;
}
Read More +

b428. 凱薩加密

Problem

背景

曾幾何時,基礎題庫已經成了不基礎的題庫。小小新手們寫個題目,不少拿了 TLE、CE 求助無門,就再也不想打開 Zerojudge。高中生哪有寫這麼困難的題目,高中生都不像高中生。在某 M 那個年代寫的題目非常簡單,沒有特別變化處理,更別說多麼高檔的資料結構,暴力算法 (naive algorithm) 就能輕鬆切題。

「年代變了呢,現在的高中生要寫出比大學生的某 M 更困難的題目」

重溫解題的那份初心吧!

題目描述

在西元前就存在的一種加密-凱薩加密為目前最早發現的替換加密 (substitution cipher)。其原理很簡單,將一段明文往替換成往後數的第 $k$ 個英文字母。

若用數學式表示凱薩加密和解密,如下:

加密 $C = E_K(P) = (P + k) \mod 26$
解密 $P = D_K(P) = (C - k) \mod 26$

例如 $k = 3$ 時,發生的情況如下:

明文字母表:ABCDEFGHIJKLMNOPQRSTUVWXYZ
密文字母表:DEFGHIJKLMNOPQRSTUVWXYZABC

從數學的觀點來看,每一個字母就是一個數字。

A = 0, B = 1, C = 2, …,X = 23, Y = 24, Z = 25

Sample Input

1
2
3
4
5
6
ABCDEFGHIJKLMNOPQRSTUVWXYZ
DEFGHIJKLMNOPQRSTUVWXYZABC
DLQXABXEEQMEUQYLZPEK
YGLSVWSZZLHZPLTGUKZF
Z
Z

Sample Output

1
2
3
3
21
0

Solution

只需要看第一個字符的變換方式即可。

1
2
3
4
5
6
7
8
9
#include <bits/stdc++.h>
using namespace std;
int main() {
char s1[1024], s2[1024];
while (scanf("%s %s", s1, s2) == 2)
printf("%d\n", (((s2[0] - 'A') - (s1[0] - 'A'))%26 + 26)%26);
return 0;
}
Read More +

POJ 1811 - Prime Test

Problem

給一個大整數 $N \le 2^{54}$,判斷是否為質數,若不是則進行因數分解,並且輸出最小的質因數。

備註:大整數分解不可使用一般的 $O(\sqrt{N})$ 算法完成。

Sample Input

1
2
3
2
5
10

Sample Output

1
2
Prime
2

Solution

以下的寫法還不是最快的,而且沒有加上最小質因數的剪枝。若在 pollard-rho 算法中,使用 Brent’s algorithm 取代 Floyd’s algorithm 進行環偵測,同時減少模數使用會變得更快 (常數上)。

討論區提供卡邁克爾數 (Carmichael Number),讓費馬素性檢驗 (Fermat primality test) 判斷失誤,若用盧卡斯-萊默檢驗法 (Miller–Rabin primality test) 則不受影響。這讓我想到寫過鄒賽爾數 (Zeisel Number) 生成,由數個質數構成大數 $N$ 能測試更多的效能瓶頸吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
#include <stdio.h>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
#define MAXL (50000>>5)+1
#define GET(x) (mark[x>>5]>>(x&31)&1)
#define SET(x) (mark[x>>5] |= 1<<(x&31))
int mark[MAXL];
int P[50000], Pt = 0;
void sieve() {
register int i, j, k;
SET(1);
int n = 46340;
for (i = 2; i <= n; i++) {
if (!GET(i)) {
for (k = n/i, j = i*k; k >= i; k--, j -= i)
SET(j);
P[Pt++] = i;
}
}
}
long long mul(unsigned long long a, unsigned long long b, unsigned long long mod) {
long long ret = 0;
for (a %= mod, b %= mod; b != 0; b >>= 1, a <<= 1, a = a >= mod ? a - mod : a) {
if (b&1) {
ret += a;
if (ret >= mod) ret -= mod;
}
}
return ret;
}
void exgcd(long long x, long long y, long long &g, long long &a, long long &b) {
if (y == 0)
g = x, a = 1, b = 0;
else
exgcd(y, x%y, g, b, a), b -= (x/y) * a;
}
long long llgcd(long long x, long long y) {
if (x < 0) x = -x;
if (y < 0) y = -y;
if (!x || !y) return x + y;
long long t;
while (x%y)
t = x, x = y, y = t%y;
return y;
}
long long inverse(long long x, long long p) {
long long g, b, r;
exgcd(x, p, g, r, b);
if (g < 0) r = -r;
return (r%p + p)%p;
}
long long mpow(long long x, long long y, long long mod) { // mod < 2^32
long long ret = 1;
while (y) {
if (y&1)
ret = (ret * x)%mod;
y >>= 1, x = (x * x)%mod;
}
return ret % mod;
}
long long mpow2(long long x, long long y, long long mod) {
long long ret = 1;
while (y) {
if (y&1)
ret = mul(ret, x, mod);
y >>= 1, x = mul(x, x, mod);
}
return ret % mod;
}
int isPrime(long long p) { // implements by miller-babin
if (p < 2) return 0;
if (p == 2) return 1;
if (!(p&1)) return 0;
long long q = p-1, a, t;
int k = 0, b = 0;
while (!(q&1)) q >>= 1, k++;
for (int it = 0; it < 2; it++) {
a = rand()%(p-4) + 2;
t = mpow2(a, q, p);
b = (t == 1) || (t == p-1);
for (int i = 1; i < k && !b; i++) {
t = mul(t, t, p);
if (t == p-1)
b = 1;
}
if (b == 0)
return 0;
}
return 1;
}
long long pollard_rho(long long n, long long c) {
register long long x = 2, y = 2, d;
do {
x = mul(x, x, n)+c;
if (x >= n) x -= n;
y = mul(y, y, n)+c;
if (y >= n) y -= n;
y = mul(y, y, n)+c;
if (y >= n) y -= n;
d = llgcd(x-y, n);
if(d > 1) return d;
} while(true);
return n;
}
void factorize(int n, vector<long long> &f) {
for (int i = 0; i < Pt && P[i]*P[i] <= n; i++) {
if (n%P[i] == 0) {
while (n%P[i] == 0)
f.push_back(P[i]), n /= P[i];
}
}
if (n != 1) f.push_back(n);
}
void llfactorize(long long n, vector<long long> &f, int i = 2) {
if (n == 1)
return ;
if (n < 1e+9) {
factorize(n, f);
return ;
}
if (isPrime(n)) {
f.push_back(n);
return ;
}
long long d = n;
for (; d == n; i++)
d = pollard_rho(n, i);
llfactorize(d, f, i);
llfactorize(n/d, f, i);
}
int main() {
sieve();
int testcase;
scanf("%d", &testcase);
while (testcase--) {
long long n;
scanf("%lld", &n);
vector<long long> f;
llfactorize(n, f);
sort(f.begin(), f.end());
if (f.size() == 1)
puts("Prime");
else
printf("%lld\n", f[0]);
}
return 0;
}
Read More +

a761. 罐頭的記憶體

Problem

模擬作業系統的 dynamic memory allocation,

  • 宣告連續一段記憶體配置給某個程序,並且標記辨識碼。
  • 若宣告失敗,則輸出碰撞區域中,辨識碼最小的數字。
  • 詢問記憶體存取時,是否合法,若合法輸出程序的辨識碼。

Sample Input

1
2
3
4
5
6
7
8
7
load 29
map 1 25 39
map 2 23 24
map 3 22 25
map 4 25 40
store 33
store 22

Sample Output

1
2
3
4
5
6
7
fail to load from 29
region 1 created
region 2 created
fail to create region 3, overlap with region 1
fail to create region 4, overlap with region 1
store region to 1
fail to store to 22

Solution

溫馨提示:此題的測資隨機,單純離散化套用二分查找,編號最小使用線性搜索即可通過。by 蔡神 asas

儘管直觀作法可以通過,來討論看看別種作法吧。

  • 線段樹 + 延遲標記 + 離線處理
    離散結果最多有 $O(N + Q)$ 的離散點,單筆操作複雜度為 $O(\log Q)$,空間複雜度 $O(Q)$,空間消耗量相較於其他算法最多 (附加延遲標記的使用)。
  • 分塊算法 + 離線處理
    利用分塊 Unrolled Linked List 的快取優勢來降複雜度,空間複雜度仍然是 $O(Q)$,單筆操作複雜度為 $O(\sqrt{Q})$。空間複雜度的常數比線段樹少一半以上,別忘詢問離線是共同的空間成本。
  • 分塊在線
    直接宣告 $O(2^{32})$ 進行配置,因此分塊大小為 $O(\sqrt{2^{32}})$。將配置連續區段用區間 $[l, r, pid]$ 的方式儲存在塊之中,用一個平衡樹維護每一個塊的狀態。由於要輸出編號最小的,詢問複雜度至少為 $O(65536)$,為了加速詢問可以利用剪枝 (塊的最小值仍大於當前的答案) 來完成。空間複雜度跟實際情況有關,這一做法在當前數據中是最快,記憶體最小的一種辦法,因為在線很多詢問點是沒必要實際存在的。

由於這一題沒有釋放記憶體操作,從均攤上可以清楚,分塊離線的修改離散點的次數最多為 $O(Q)$,也就是每一個點均被塗色僅僅一次,剩下就是在詢問上加速。

線段樹

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1<<21, INF = 0x3f3f3f3f;
class SegmentTree {
public:
struct Node {
int val, del;
void init(int a = 0, int b = 0) {
val = a, del = b;
}
} nodes[MAXN];
void pushDown(int k, int l, int r) {
if (nodes[k].del != INF) {
nodes[k<<1].val = min(nodes[k<<1].val, nodes[k].del);
nodes[k<<1].del = min(nodes[k<<1].del, nodes[k].del);
nodes[k<<1|1].val = min(nodes[k<<1|1].val, nodes[k].del);
nodes[k<<1|1].del = min(nodes[k<<1|1].del, nodes[k].del);
nodes[k].del = INF;
}
}
void pushUp(int k, int l, int r) {
nodes[k].val = min(nodes[k<<1].val, nodes[k<<1|1].val);
}
void build(int k, int l, int r) {
nodes[k].init(INF, INF);
if (l == r) return ;
int mid = (l + r)/2;
build(k<<1, l, mid);
build(k<<1|1, mid+1, r);
pushUp(k, l, r);
}
void assignUpdate(int k, int l, int r, int val) {
nodes[k].del = min(nodes[k].del, val);
nodes[k].val = min(nodes[k].val, val);
}
void assign(int k, int l, int r, int x, int y, int val) {
if (x <= l && r <= y) {
assignUpdate(k, l, r, val);
return;
}
pushDown(k, l, r);
int mid = (l + r)/2;
if (x <= mid) assign(k<<1, l, mid, x, y, val);
if (y > mid) assign(k<<1|1, mid+1, r, x, y, val);
pushUp(k, l, r);
}
int query(int k, int l, int r, int x) {
if (x <= l && r <= x || nodes[k].val == INF)
return nodes[k].val;
pushDown(k, l, r);
int mid = (l + r)/2, ret;
if (x <= mid) ret = query(k<<1, l, mid, x);
else ret = query(k<<1|1, mid+1, r, x);
pushUp(k, l, r);
return ret;
}
int query(int k, int l, int r, int x, int y) {
if (x <= l && r <= y || nodes[k].val == INF)
return nodes[k].val;
pushDown(k, l, r);
int mid = (l + r)/2, ret = INF;
if (x <= mid)
ret = min(ret, query(k<<1, l, mid, x, y));
if (y > mid)
ret = min(ret, query(k<<1|1, mid+1, r, x, y));
pushUp(k, l, r);
return ret;
}
} tree;
const int MAXQ = 524288;
char cmd[MAXQ][8];
int args[MAXQ][3];
int main() {
int N;
while (scanf("%d", &N) == 1) {
map<int, int> R;
for (int i = 0; i < N; i++) {
scanf("%s", &cmd[i]);
int a, b, c;
if (cmd[i][0] == 'l')
scanf("%d", &a), R[a] = 0;
else if (cmd[i][0] == 'm')
scanf("%d %d %d", &a, &b, &c), R[b] = R[c] = 0;
else
scanf("%d", &a), R[a] = 0;
args[i][0] = a, args[i][1] = b, args[i][2] = c;
}
int size = 0;
for (auto &x : R)
x.second = ++size;
tree.build(1, 1, size);
for (int i = 0; i < N; i++) {
int a, b, c, t;
a = args[i][0], b = args[i][1], c = args[i][2];
if (cmd[i][0] == 'l') {
t = tree.query(1, 1, size, R[a]);
if (t == INF)
printf("fail to load from %d\n", a);
else
printf("load from region %d\n", t);
} else if (cmd[i][0] == 'm') {
t = tree.query(1, 1, size, R[b], R[c]);
if (t != INF)
printf("fail to create region %d, overlap with region %d\n", a, t);
else
printf("region %d created\n", a), tree.assign(1, 1, size, R[b], R[c], a);
} else {
t = tree.query(1, 1, size, R[a]);
if (t == INF)
printf("fail to store to %d\n", a);
else
printf("store to region %d\n", t);
}
}
}
return 0;
}

分塊離線

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f, MAXQ = 524288;
class Unrolled {
public:
int PILE, mask, shift;
vector< vector<int> > nodes;
vector<int> dirty;
void alloc(int size) {
nodes.clear(), dirty.clear();
for (PILE = 1, shift = 0; PILE * PILE < size; PILE <<= 1, shift++);
mask = PILE - 1;
for (int l = 0, r; l < size; l = r+1) {
r = min(l+PILE-1, size-1);
nodes.push_back(vector<int>(r-l+1, INF));
dirty.push_back(INF);
}
}
int operator[](const int x) const {
return nodes[x>>shift][x&mask];
}
int empty(int l, int r) {
int bl = l>>shift, br = r>>shift;
int ret = INF;
if (bl == br) {
for (int i = l; i <= r; i++)
ret = min(ret, (*this)[i]);
return ret;
}
for (int i = bl+1; i < br; i++)
ret = min(ret, dirty[i]);
for (int i = (bl+1) * PILE-1; i >= l; i--)
ret = min(ret, (*this)[i]);
for (int i = (br) * PILE; i <= r; i++)
ret = min(ret, (*this)[i]);
return ret;
}
void fill(int l, int r, int val) {
int bl = l>>shift, br = r>>shift;
for (int i = bl; i <= br; i++)
dirty[i] = min(dirty[i], val);
for (int i = l, bi; i <= r; i++)
nodes[i>>shift][i&mask] = val;
}
} mem;
char cmd[MAXQ][8];
int args[MAXQ][3];
int main() {
int N;
while (scanf("%d", &N) == 1) {
map<int, int> R;
for (int i = 0; i < N; i++) {
scanf("%s", &cmd[i]);
int a, b, c;
if (cmd[i][0] == 'l')
scanf("%d", &a), R[a] = 0;
else if (cmd[i][0] == 'm')
scanf("%d %d %d", &a, &b, &c), R[b] = R[c] = 0;
else
scanf("%d", &a), R[a] = 0;
args[i][0] = a, args[i][1] = b, args[i][2] = c;
}
int size = 0;
for (auto &x : R)
x.second = size++;
mem.alloc(size);
for (int i = 0; i < N; i++) {
int a, b, c, t;
a = args[i][0], b = args[i][1], c = args[i][2];
if (cmd[i][0] == 'l') {
t = mem[R[a]];
if (t == INF)
printf("fail to load from %d\n", a);
else
printf("load from region %d\n", t);
} else if (cmd[i][0] == 'm') {
t = mem.empty(R[b], R[c]);
if (t != INF)
printf("fail to create region %d, overlap with region %d\n", a, t);
else
printf("region %d created\n", a), mem.fill(R[b], R[c], a);
} else {
t = mem[R[a]];
if (t == INF)
printf("fail to store to %d\n", a);
else
printf("store to region %d\n", t);
}
}
}
return 0;
}

分塊在線

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
class Unrolled {
public:
struct Interval {
long long l, r;
int pid;
Interval(long long a = 0, long long b = 0, int c = 0):
l(a), r(b), pid(c) {}
bool operator<(const Interval &x) const {
return l < x.l || (l == x.l && r > x.r);
}
bool include(int x) const {
return l <= x && x <= r;
}
};
long long PILE, mask;
int shift;
vector< set<Interval> > nodes;
vector<int> dirty;
void alloc(long long size) {
nodes.clear(), dirty.clear();
for (PILE = 1, shift = 0; PILE * PILE < size; PILE <<= 1, shift++);
mask = PILE - 1;
for (long long l = 0, r; l < size; l = r+1) {
r = min(l+PILE-1, size-1);
nodes.push_back(set<Interval>());
dirty.push_back(INF);
}
}
int operator[](const long long x) const {
const set<Interval> &s = nodes[x>>shift];
auto it = s.upper_bound(Interval(x, x));
it = it == s.begin() ? it : --it;
return it == s.end() || !(it->include(x)) ? INF : it->pid;
}
int empty(long long l, long long r) {
int bl = l>>shift, br = r>>shift;
int ret = INF;
if (bl == br) {
set<Interval> &s = nodes[bl];
auto st = s.upper_bound(Interval(l, l));
st = st == s.begin() ? st : --st;
for (auto it = st; it != s.end(); it++) {
if (max(l, it->l) <= min(r, it->r))
ret = min(ret, it->pid);
if (it->l > r)
break;
}
return ret;
}
for (int i = bl+1; i < br; i++)
ret = min(ret, dirty[i]);
for (auto it = nodes[bl].rbegin(); it != nodes[bl].rend() && ret > dirty[bl]; it++) {
if (max(l, it->l) <= min(r, it->r)) {
ret = min(ret, it->pid);
} else {
break;
}
}
for (auto it = nodes[br].begin(); it != nodes[br].end() && ret > dirty[br]; it++) {
if (max(l, it->l) <= min(r, it->r)) {
ret = min(ret, it->pid);
} else {
break;
}
}
return ret;
}
void fill(long long l, long long r, int val) {
int bl = l>>shift, br = r>>shift;
for (int i = bl; i <= br; i++)
dirty[i] = min(dirty[i], val);
if (bl == br) {
nodes[bl].insert(Interval(l, r, val));
return ;
}
for (int i = bl+1; i < br; i++)
nodes[i].insert(Interval(i*PILE, (i+1)*PILE-1, val));
nodes[bl].insert(Interval(l, (bl+1) * PILE-1, val));
nodes[br].insert(Interval(br*PILE, r, val));
}
} mem;
int main() {
int N;
char cmd[8];
while (scanf("%d", &N) == 1) {
mem.alloc(1LL<<31);
for (int i = 0; i < N; i++) {
scanf("%s", &cmd);
int a, b, c, t;
if (cmd[0] == 'l') {
scanf("%d", &a);
t = mem[a];
if (t == INF)
printf("fail to load from %d\n", a);
else
printf("load from region %d\n", t);
} else if (cmd[0] == 'm') {
scanf("%d %d %d", &a, &b, &c);
t = mem.empty(b, c);
if (t != INF)
printf("fail to create region %d, overlap with region %d\n", a, t);
else
printf("region %d created\n", a), mem.fill(b, c, a);
} else {
scanf("%d", &a);
t = mem[a];
if (t == INF)
printf("fail to store to %d\n", a);
else
printf("store to region %d\n", t);
}
}
}
return 0;
}
Read More +

b436. 圖片的梯度再進化

Problem

可以參考 中央大學影像處理實驗室 課程 講義第八章 邊擷取

除了索貝爾運算 Sobel operator,還有如 Prewitt operator、Kirsch operator … 等,來進行邊擷取。

Sample Input

1
2
3
2 2
0 0 0 255 255 255
255 255 255 255 255 255

Sample Output

1
2
3
2 2
191 191 191 128 128 128
128 128 128 64 64 64

Solution

比較麻煩是在處理邊緣擴充,寫一個函數去特判延伸的 pixel 位置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <bits/stdc++.h>
using namespace std;
class IMAGE {
public:
struct Pixel {
double r, g, b, a;
Pixel(double x = 0, double y = 0, double z = 0, double w = 0):
r(x), g(y), b(z), a(w) {}
void read() {
scanf("%lf %lf %lf", &r, &g, &b);
}
Pixel operator-(const Pixel &x) const {
return Pixel(r-x.r, g-x.g, b-x.b, a-x.a);
}
Pixel operator+(const Pixel &x) const {
return Pixel(r+x.r, g+x.g, b+x.b, a+x.a);
}
Pixel operator*(const double x) const {
return Pixel(r*x, g*x, b*x, a*x);
}
Pixel operator/(const double x) const {
return Pixel(r/x, g/x, b/x, a/x);
}
void print() {
printf("%d %d %d", (int)round(r), (int)round(g), (int)round(b));
}
};
int W, H;
static const int MAXN = 256;
Pixel data[MAXN][MAXN], tmp[MAXN][MAXN];
void read() {
scanf("%d %d", &W, &H);
for (int i = 0; i < H; i++)
for (int j = 0; j < W; j++)
data[i][j].read();
}
Pixel pabs(Pixel x) {
return Pixel(fabs(x.r), fabs(x.g), fabs(x.b));
}
Pixel getPixel(int x, int y) {
if (x >= 0 && y >= 0 && x < H && y < W)
return data[x][y];
if (y < 0) return data[min(max(x, 0), H-1)][0];
if (y >= W) return data[min(max(x, 0), H-1)][W-1];
if (x < 0) return data[0][min(max(y, 0), W-1)];
if (x >= H) return data[H-1][min(max(y, 0), W-1)];
return Pixel(0, 0, 0);
}
void border() {
const int dx[] = {-1, -1, -1, 0, 0, 0, 1, 1, 1};
const int dy[] = {-1, 0, 1, -1, 0, 1, -1, 0, 1};
const int xw[] = {-1, 0, 1, -2, 0, 2, -1, 0, 1};
const int yw[] = {-1, -2, -1, 0, 0, 0, 1, 2, 1};
for (int i = 0; i < H; i++) {
for (int j = 0; j < W; j++) {
Pixel Dx(0, 0, 0), Dy(0, 0, 0);
for (int k = 0; k < 9; k++) {
Dx = Dx + getPixel(i+dx[k], j+dy[k]) * xw[k];
Dy = Dy + getPixel(i+dx[k], j+dy[k]) * yw[k];
}
Dx = pabs(Dx), Dy = pabs(Dy);
tmp[i][j] = (Dx + Dy)/8.0;
}
}
for (int i = 0; i < H; i++)
for (int j = 0; j < W; j++)
data[i][j] = tmp[i][j];
}
void print() {
printf("%d %d\n", W, H);
for (int i = 0; i < H; i++)
for (int j = 0; j < W; j++)
data[i][j].print(), printf("%c", j == W-1 ? '\n' : ' ');
}
} test;
int main() {
test.read();
test.border();
test.print();
return 0;
}
Read More +