fsy's blog

燃峥嵘岁月,烈日终破云。

常州一中训练试题泛做 Part 1

本系列不定期更新,每个 Part 约有 $6 \sim 8$ 题,一般一个专题一个 Part,具体题目数量视标签个数而定。

8/5 T1 絶対、大丈夫!

设原数为 $\overline{a_1a_2a_3 \cdots a_n}$,明显,$P = \sum_{i=1}^{n} a_i \times (10^i-10^{p_i})$,其中 ${p_1, p_2, \cdots, p_n}$ 是 $1 \sim n$ 的一个排列。

经过简单推导易证对于任意 $x, y \in \mathbb{N}$,$(10^x-10^y) \bmod 9 = 0$,故 $P \bmod 9 = 0$。

能被 $9$ 整除的数各位数加起来一定为 $9$ 的倍数。又因为删去的一位数不为 $0$,所以删去的那一位数是唯一的。答案即为 $9 - P \text{的数位和} \bmod 9$。

再考虑构造解。由于 $P \bmod 9 = 0$,于是设 $T = \frac{P}{9}$(带前导零),$S=10T$,容易证明 $S-T=P$ 且组成 $S$,$T$ 的数字相同。由于允许前导零,直接输出 $S$,$T$ 即可。

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
#include <bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define LD long double
#define _rep(i, x, y) for(int i = x; i <= y; i++)
#define _per(i, x, y) for(int i = x; i >= y; i--)
template <typename T>
inline void read(T &x) {
x = 0; T f = (T) 1;
char c = getchar();
for (; !isdigit(c); c = getchar())
if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - 48;
x *= f;
}
template <typename T>
inline void write(T x) {
if(x < 0) {putchar('-'); x = -x;}
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T>
inline void writesp(T x, char sp = '\n') {
write(x); putchar(sp);
}
const int maxN = 5e5 + 10, maxM = 1e6 + 10;
std::string P;
int n, len;
int Delta[maxN];
void divide() {
int q = 0, r = 0;
_rep(i, 1, len + 1) {
r = r * 10 + Delta[i];
Delta[i] = r / 9;
r %= 9;
}
}
int main() {
freopen("yukikaze.in", "r", stdin);
freopen("yukikaze.out", "w", stdout);
std::cin >> P; int cnt = 0; len = P.size();
_rep(i, 0, len - 1) {cnt = (cnt + P[i] - '0') % 9;}
_rep(i, 0, len - 1) {Delta[i + 2] = P[i] - '0';}
putchar(9 - cnt + '0'); std::cout << "\n"; Delta[1] = 9 - cnt;
divide();
int st = 1;
while(!Delta[st]) st++;
_rep(i, st, len + 1) std::cout << Delta[i];
std::cout << "0\n0";
_rep(i, st, len + 1) std::cout << Delta[i];
std::cout << "\n";
return 0;
}

8/6 T2 まるゆ,潜航 !

设 $1 \sim n-1$ 号まるゆ运输的货物的类型的并集 $U$ 大小为 $u$,交集 $V$ 大小为 $v$,明显有 $0 \leq v \leq u \leq k-1$。

答案即为:

$$
\sum_{u=0}^{k-1} \sum_{v=0}^{u} \binom{k}{u} \binom{u}{v} \left( 2^{n-1}-2 \right)^{u-v} 2^{k-v} (2^k-2^u)
$$

对每个部分单独解释:

  • $\binom{k}{u}$、$\binom{u}{v}$:从 $k$ 个货物里选 $u$ 个作为并集,从 $u$ 个货物里选 $v$ 个作为交集。
  • $(2^{n-1}-2)^{u-v}$:分成三种情况考虑。
    • 1、对于交集内的物品:$1 \sim n-1$ 号まるゆ必须全部选这个物品。
    • 2、对于不在并集内的物品:$1 \sim n-1$ 号まるゆ必须都不选这个物品。
    • 3、对于在并集,不在交集内的物品:$1 \sim n-1$ 号まるゆ不能都选或都不选这个货物,剩下方案随意,故此部分贡献为 $(2^{n-1}-2)^{u-v}$。
  • $2^{k-v}$:对于 $0$ 号まるゆ,他不能选 $V$ 内的物品,否则就不满足限制 1。剩余货物随意,故此部分贡献为 $2^{k-v}$。
  • $2^k-2^u$:对于 $n$ 号まるゆ,他不能同时选择不在 $U$ 内的物品,否则就不满足限制 2。剩下货物随意,故此部分贡献为 $(2^{k-u}-1) \times 2^u = 2^k - 2^u$。

接下来就是化简,疯狂二项式定理,一顿操作猛如虎:

$$
\begin{align}
Ans = & \sum_{u=0}^{k-1} \sum_{v=0}^{u} \binom{k}{u} \binom{u}{v} (2^{n-1}-2)^{u-v} 2^{k-v}(2^k-2^u)\\
= & \sum_{u=0}^{k-1} \binom{k}{u}(2^k-2^u)2^k \sum_{v=0}^{u} \binom{u}{v} (2^{n-1}-2)^{u-v} \left(2^{-1}\right)^v\\
= & \sum_{u=0}^{k-1} \binom{k}{u}(2^k-2^u)2^k(2^{n-1}-2+2^{-1})^u\\
= & 4^k \times \sum_{u=0}^{k-1} \binom{k}{u} (2^{n-1}-2+2^{-1})^u 1^{k-u}-2^k \times \sum_{u=0}^{k-1} \binom{k}{u} (2^n-3)^u 1^{k-u}\\
= & 4^k \times [(2^{n-1}-1+2^{-1})^k-(2^{n-1}-2+2^{-1})^k] - 2^k [(2^{n}-2)^k-(2^{n}-3)^k]\\
= & (2^{n+1}-2)^k-(2^{n+1}-6)^k-(2^{n+1}-4)^k+(2^{n+1}-6)^k\\
= & (2^{n+1}-2)^k-(2^{n+1}-4)^k\\
\end{align}
$$

快速幂计算即可。

时间复杂度:$\mathcal{O}(T \log^2 k)$。

代码:

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
#include <bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define LD long double
#define _rep(i, x, y) for(int i = x; i <= y; i++)
#define _per(i, x, y) for(int i = x; i >= y; i--)
template <typename T>
inline void read(T &x) {
x = 0; T f = (T) 1; char c = getchar();
for(; !isdigit(c); c = getchar()) {if(c == '-') f = -f;}
for(; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T>
inline void write(T x) {
if(x < 0) {x = -x; putchar('-');}
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T>
inline void writesp(T x, char sp = ' ') {
write(x); putchar(sp);
}
const int P = 1e9 + 7, maxN = 3e3 + 10;
int T;
LL n, k;
int fac[maxN], facinv[maxN], powtwo[maxN];
int pow_mod(int a, LL b, int p) {
int sum = 1;
while(b) {
if(b & 1ll) sum = 1ll * sum * a % p;
a = 1ll * a * a % p;
b >>= 1ll;
}
return sum;
}
int main() {
freopen("maruyu.in", "r", stdin);
freopen("maruyu.out", "w", stdout);
read(T);
while(T--) {
read(n); read(k);
int Q = pow_mod(2, n + 1, P);
int ans = (pow_mod((Q - 2 + P) % P, k, P) - pow_mod((Q - 4 + P) % P, k, P) + P) % P;
writesp(ans, '\n');
}
return 0;
}

9/14 T2 大凤号装甲空母

先给出一种比较友好的思考方法。

设 $x = \frac{\sqrt{5}+1}{2}$,使用计算器计算 $x^n$,可以发现几个性质:

  • $x^n + x^{n+1} = x^{n+2}$
  • $|x^n - \operatorname{round}(x^n)|$ 逐渐变小,其中 $\operatorname{round}(a)$ 表示四舍五入。

设 $f(x) = \lfloor x^n \rfloor$,$\epsilon(x) = [x \bmod 2 = 1]$,再经过仔细观察后不难得到:$f(x) = f(x - 1) + f(x - 2) + \epsilon(x)$。

如果舍去 $\epsilon(x)$ 一项,则该式是经典的 Fibonacci 数列,可以用矩阵乘法求出。但其实 $\epsilon(x)$ 函数有性质 $\epsilon(x) = \epsilon(x - 2)$,于是乎:
$$
\begin{bmatrix}
f(n-1) & f(n) & \epsilon(x-1) & \epsilon(x)
\end{bmatrix}
=
\begin{bmatrix}
f(n-2) & f(n-1) & \epsilon(x-2) & \epsilon(x-1)
\end{bmatrix}
\begin{bmatrix}
0 & 1 & 0 & 0 \\
1 & 1 & 0 & 0 \\
0 & 1 & 0 & 1 \\
0 & 0 & 1 & 0 \\
\end{bmatrix}
$$
矩阵快速幂即可,时间复杂度 $\mathcal{O}(4^3 \times \log 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
#include <bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define LD long double
#define reg register
#define _rep(i, x, y) for(int i = x; i <= y; i++)
#define _per(i, x, y) for(int i = x; i >= y; i--)
template <typename T>
inline void read(T &x) {
x = 0; T f = (T) 1;
char c = getchar();
for(; !isdigit(c); c = getchar()) {if(c == '-') f = -f;}
for(; isdigit(c); c = getchar()) x = x * 10 + c - 48;
x *= f;
}
template <typename T>
inline void write(T x) {
if(x < 0) {x = -x; putchar('-');}
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T>
inline void writesp(T x, char sp = ' ') {write(x); putchar(sp);}
LL n;
int p;
int Pows[100] = {1, 1, 2, 4, 6, 11, 17, 29, 46, 76, 122, 199, 321, 521, 842, 1364, 2206, 3571, 5777, 9349, 15126};
int F[7], a[7][7], f[7], c[7][7];
void Fib() {
memset(f, 0, sizeof(f));
_rep(j, 0, 3) {
_rep(k, 0, 3) {
f[j] = (f[j] + 1ll * F[k] * a[k][j] % p) % p;
}
}
_rep(i, 0, 3) F[i] = f[i];
}
void square() {
memset(c, 0, sizeof(c));
_rep(i, 0, 3) {
_rep(j, 0, 3) {
_rep(k, 0, 3) {
c[i][j] = (c[i][j] + 1ll * a[i][k] * a[k][j] % p) % p;
}
}
}
_rep(i, 0, 3) {
_rep(j, 0, 3) {
a[i][j] = c[i][j];
}
}
}
int main() {
freopen("taiho.in", "r", stdin);
freopen("taiho.out", "w", stdout);
read(n); read(p);
if(p == 1) {
writesp(0, '\n');
return 0;
} else if(n <= 1e6) {
if(n <= 20) {
writesp(Pows[n] % p, '\n'); return 0;
}
int l1 = 1, l2 = 2, l3 = 4;
_rep(i, 4, n) {
l1 = l2; l2 = l3;
l3 = (l1 + l2 + (i % 2 == 1)) % p;
}
printf("%d\n", l3);
} else {
F[0] = 1; F[1] = 1; F[2] = 1; F[3] = 0;
a[0][0] = 0; a[1][0] = 1; a[2][0] = 0; a[3][0] = 0;
a[0][1] = 1; a[1][1] = 1; a[2][1] = 0; a[3][1] = 1;
a[0][2] = 0; a[1][2] = 0; a[2][2] = 0; a[3][2] = 1;
a[0][3] = 0; a[1][3] = 0; a[2][3] = 1; a[3][3] = 0;
while(n) {
if(n & 1) Fib();
square(); n >>= 1;
}
writesp(F[0], '\n');
}
return 0;
}

std 的特征根法得咕一会…

8/7 T3 最大割

Hexo nmd 竟然不支持 \bold{}

首先,期望就等于边权 $\times$ 两个点不在同一集合的概率。

列出式子可得:
$$
\begin{align}
E(maxcut) = & \sum_{(i,j) \in E} a_{i,j} \times \left[\left(\frac{1}{2} \times (1 + \varphi_s(\langle \mathbf{u_j, Z} \rangle))\right) \times \left(\frac{1}{2} \times (1 - \varphi_s(\langle \mathbf{u_i, Z} \rangle))\right) + \left(\frac{1}{2} \times (1 + \varphi_s(\langle \mathbf{u_i, Z} \rangle))\right) \times \left(\frac{1}{2} \times (1 - \varphi_s(\langle \mathbf{u_j, Z} \rangle))\right)\right] \\
= & \sum_{(i,j) \in E} a_{i,j} \times \left[\frac{1}{2} \times (1-\varphi_s(\langle \mathbf{u_i, Z} \rangle)\varphi_s(\langle \mathbf{u_j, Z} \rangle)) \right]
\end{align}
$$
设 $r_i = \langle \mathbf{u_i, Z} \rangle$,将 $r_i$ 按绝对值升序排序。

注意到 $E(maxcut)$ 是一个关于 $\frac{1}{s}$ 的二次函数,且在 $[r_i, r_{i+1}]$ 中连续均匀。考虑暴力计算系数,分段求最值,单次时间复杂度 $\mathcal{O}(n^3)$,不够优秀。

再注意到在 $[r_i, r_{i+1}]$ 转移至 $[r_{i+1}, r_{i+2}]$ 时,只会有一个点的 $\varphi_s$ 值发生变化,于是只计算这一个点的影响,就能将单次时间复杂度降为 $\mathcal{O}(n^2)$。

代码:

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>
#define LL long long
#define ULL unsigned long long
#define LD long double
#define _rep(i, x, y) for(int i = x; i <= y; i++)
#define _per(i, x, y) for(int i = x; i >= y; i--)
template <typename T>
inline void read(T &x) {
x = 0; T f = (T) 1;
char c = getchar();
for (; !isdigit(c); c = getchar())
if (c == '-') f = -f;
for (; isdigit(c); c = getchar()) x = x * 10 + c - 48;
x *= f;
}
template <typename T>
inline void write(T x) {
if(x < 0) {putchar('-'); x = -x;}
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T>
inline void writesp(T x, char sp = ' ') {
write(x); putchar(sp);
}
const int maxN = 151, maxQ = 1010;
const double INF = 1e14;
int n, q;
double a[maxN][maxN], u[maxN][maxN], Z[maxN];
double A, B, C, product[maxN], ccf;
struct node {
double pro;
int id;
} r[maxN];
bool cmp(node A, node B) {return fabs(A.pro) < fabs(B.pro);}
double sgn(double S) {return S > 0 ? 1 : -1;}
int main() {
freopen("maxcut.in", "r", stdin);
freopen("maxcut.out", "w", stdout);
read(n); read(q);
_rep(i, 1, n) {
_rep(j, 1, n) scanf("%lf", &a[i][j]), ccf += a[i][j] * (i < j);
}
_rep(i, 1, n) {
_rep(j, 1, n) scanf("%lf", &u[i][j]);
}
_rep(i, 1, q) {
A = 0, B = 0, C = 0;
_rep(j, 1, n) scanf("%lf", &Z[j]);
_rep(j, 1, n) {
r[j].id = j; product[j] = 0;
_rep(k, 1, n) {product[j] += u[j][k] * Z[k];}
r[j].pro = product[j];
}
std::sort(r + 1, r + n + 1, cmp); r[n + 1].pro = INF;
_rep(j, 1, n) {
_rep(k, 1, j - 1) C += sgn(product[j]) * sgn(product[k]) * a[j][k];
}
double ans = C;
_rep(j, 1, n) {
double qwq = fabs(r[j].pro), qaq = fabs(r[j + 1].pro);
int f = r[j].id;
_rep(k, 1, n) {
if(k == f) continue;
if(fabs(product[k]) < qwq) {
B -= product[k] * sgn(product[f]) * a[f][k];
A += product[k] * product[f] * a[f][k];
} else {
C -= sgn(product[k]) * sgn(product[f]) * a[f][k];
B += sgn(product[k]) * product[f] * a[f][k];
}
}
if(qwq == 0) continue;
std::swap(qwq, qaq);
qwq = 1.0 / qwq; qaq = 1.0 / qaq;
double m = 0;
if(A == 0) {
m = (B < 0 ? qaq : qwq);
} else {
double axis = -B * 1.0 / (2 * A);
if(axis >= qaq) m = (A < 0 ? qwq : qaq);
else if(axis <= qwq) m = (A < 0 ? qaq : qwq);
else {
m = (A > 0 ? axis : (qaq - axis > axis - qwq ? qaq : qwq));
}
}
double fuk = (LD)(A * m * m + B * m + C);
ans = std::min(ans, fuk);
}
printf("%.10f\n", 0.5 * (ccf - ans));
}
return 0;
}

8/12 T3 Math

提高组出 Miller-Rabin,出题人真厉害

考虑 Miller-Rabin + Pollard-rho,但是如果出题人恶意构造数据的话 Pollard-rho 会退化为 $\mathcal{O}(\sqrt{n})$。

所以考虑以下思路:先将 $a_i$ 所有 $10^6$ 以内的因子筛除,那么筛除过后,$a_i$ 的值就只会有三种情况:(1)质数,(2)质数的完全平方,(3)两个不同质数的积

但是直接计数的话会重复计算在所有 $a_i$ 中出现多次的质数,所以我们还需要先筛除所有出现多次的质数。具体实现很简单,因为现在 $a_i$ 最多由两个质数相乘而得,所以两两之间求 $\gcd$ 就相当于计算哪些质数出现了多次。

再筛去这些质数后,就可以直接计数求贡献了。

时间复杂度 $\mathcal{O}(10^6 + cn \log^3 SIZ + n^2 \log SIZ)$,其中 $c$ 为 Miller-Rabin 的测试次数,$SIZ$ 为值域 $10^{18}$。

注意:两两求 $\gcd$ 时求出的 $\gcd$ 可能不是质数,一定要注意这一情况!

代码:

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
#include <bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define LD long double
#define _rep(i, x, y) for(int i = x; i <= y; i++)
#define _per(i, x, y) for(int i = x; i >= y; i--)
template <typename T>
inline void read(T &x) {
x = 0; T f = (T) 1;
char c = getchar();
for(; !isdigit(c); c = getchar()) {if(c == '-') f = -f;}
for(; isdigit(c); c = getchar()) x = x * 10 + c - '0';
x *= f;
}
template <typename T>
inline void write(T x) {
if(x < 0) {x = -x; putchar('-');}
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T>
inline void writesp(T x, char sp = ' ') {
write(x); putchar(sp);
}
const int maxN = 1e6 + 10, BLK = 1e6, P = 1e9 + 7;
int vis[maxN], primes[maxN], siz = 0, zzjakioi = 0;
int n;
LL a[510], qaq[250001];
void sieve(int n) {
memset(vis, 0, sizeof(vis));
_rep(i, 2, n) {
if(!vis[i]) {primes[++siz] = i; vis[i] = i;}
for(int j = 1; j <= siz; j++) {
if(primes[j] > vis[i] || primes[j] > n/i) break;
vis[i * primes[j]] = primes[j];
}
}
}
int tests[13] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
LL mul_mod(LL a, LL b, LL p) {
a %= p; b %= p;
LL c = (LD) a * b / p;
LL ans = a * b - c * p;
if(ans < 0) ans += p;
else if(ans >= p) ans -= p;
return ans;
}
LL pow_mod(LL a, LL b, LL p) {
LL ans = 1;
while(b) {
if(b & 1) ans = mul_mod(ans, a, p);
a = mul_mod(a, a, p);
b >>= 1;
}
return ans;
}
bool miller_rabin(LL x) {
LL rem = x - 1; int cnt = 0;
while(!(rem & 1)) {rem >>= 1; cnt++;}
_rep(i, 0, 11) {
if(x == tests[i]) return true;
if(x % tests[i] == 0) return false;
LL qwq = pow_mod(tests[i], rem, x);
LL y = qwq;
_rep(i, 1, cnt) {
qwq = mul_mod(qwq, qwq, x);
if(qwq == 1 && y != 1 && y != x - 1) return false;
y = qwq;
}
if(qwq != 1) return false;
}
return true;
}
LL gcd(LL a, LL b) {
if(!a || !b) return a + b;
return gcd(b, a % b);
}
int main() {
freopen("math.in", "r", stdin);
freopen("math.out", "w", stdout);
read(n);
_rep(i, 1, n) {
read(a[i]);
}
sieve(BLK);
int ans = 1;
_rep(i, 1, siz) {
int cnt = 0;
_rep(j, 1, n) {
while(a[j] % primes[i] == 0) {
cnt++; a[j] /= primes[i];
}
}
ans = 1ll * ans * (cnt + 1) % P;
}
_rep(i, 1, n) {
_rep(j, 1, n) {
if(i == j) continue;
LL k = gcd(a[i], a[j]);
if(k != 1) qaq[++zzjakioi] = k;
}
}
std::sort(qaq + 1, qaq + zzjakioi + 1);
int len = std::unique(qaq + 1, qaq + zzjakioi + 1) - qaq - 1;
_rep(i, 1, len) {
int cnt = 0;
_rep(j, 1, n) {
while(!(a[j] % qaq[i])) {
a[j] /= qaq[i]; cnt++;
}
}
if(miller_rabin(qaq[i])) ans = 1ll * ans * (cnt + 1) % P;
else ans = 1ll * ans * (cnt + 1) % P * (cnt + 1) % P;
}
_rep(i, 1, n) {
if(a[i] != 1) {
if(miller_rabin(a[i])) {
ans = 2ll * ans % P;
} else {
int k = (int)(sqrt(a[i]));
if(1ll * k * k == a[i]) {
ans = 3ll * ans % P;
} else {
ans = 4ll * ans % P;
}
}
}
}
writesp(ans, '\n');
return 0;
}