VP 2023 JSCPC

2023 Jiangsu Collegiate Programming Contest
2023 National Invitational of CCPC (Hunan)
The 13th Xiangtan Collegiate Programming Contest

比赛链接

Problems AC
A. Today’s Word
B. Honkai in TAIKULA
C. GG and YY’s Game
D. Star Rail
E. LCM Plus GCD
F. Timaeus
G. Moving Boxes
H. Neil’s Machine
I. Elevator
J. Similarity (Easy Version)
K. Similarity (Hard Version)
L. Architect

I 签到

1
2
3
4
void solve() {
int n = read(), m = read();
cout << n - m + 1 << '\n';
}

H 差分

挺好玩的其实,像个 div2 c 题。

区间加相当于差分数组上的 2 点修改。

后缀加相当于差分数组上的 单点修改。

判断差分数组在 mod 26 意义下是否相等即可

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
void solve() {
int n;
cin >> n;

vector<char> a(n + 1);
vector<char> b(n + 1);

vector<int> da(n + 1);
vector<int> db(n + 1);

for (int i = 1; i <= n; i++) {
// a[i] = read();
cin >> a[i];
da[i] = (a[i] - a[i - 1] + 26) % 26;
}

for (int i = 1; i <= n; i++) {
// b[i] = read();
cin >> b[i];
db[i] = (b[i] - b[i - 1] + 26) % 26;
}

int ans = 0;
for (int i = 1; i <= n; i++) {
if (da[i] != db[i]) {
ans++;
}
}

cout << ans << "\n";
}

J 最长公共子串

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
void solve() {
int n = read();

vector<string> s(n);

for (int i = 0; i < n; i++) {
cin >> s[i];
}

int ans = 0;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {

int la = s[i].size();
int lb = s[j].size();

vector dp(la + 1, vector<int>(lb + 1));

for (int x = la - 1; x >= 0; x--) {
for (int y = lb - 1; y >= 0; y--) {

if (s[i][x] == s[j][y]) {
dp[x][y] = 1 + dp[x + 1][y + 1];
}

ans = max(ans, dp[x][y]);
}
}
}
}

cout << ans << '\n';
}

A 字符串循环移位

和 24 ccpc 网络赛那个题很像,但是做法不太一样。

长度 >= 2m 之后最后 m 个字符就确定了,之后只是会一直循环移位。

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
void solve() {
int n = read(), m = read();

string s;
cin >> s;

int p = 16;

int len = s.size();
while (len < 2 * m) {

int m = len / 2;

s = s.substr(0, m) + s + s.substr(m);
len *= 2;

for (int i = len - m; i < len; i++) {
s[i] = s[i] == 'z' ? 'a' : s[i] + 1;
}
// cout << s << "\n";

p = (p + 25) % 26;
}

s = s.substr(len - m);
for (auto &ch : s) {
int t = ch - 'a';
t = (t + p) % 26;
ch = 'a' + t;
}

cout << s;
}

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
signed main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
// int T;
// cin >> T;
// for(;T--;solve());

int n, m, k;
cin >> n >> m >> k;

if(m == 0) {
if(n <= 26) {
cout << "Yes" << endl;
for(int i = 0;i < n;i ++) {
for(int j = 0;j < k;j ++) {
cout << char(i + 'a');
}
cout << endl;
}
}else {
cout << "No" << endl;
}
return 0;
}
if(m < k) {
cout << "Yes" << endl;
char s1 = 'b', s2 = 'c';
for(int i = 0;i < n;i ++) {
for(int j = 0;j < m - 1;j ++) {
cout << "a";
}
for(int j = m - 1;j < k;j ++) {
cout << ((j - m + 1) % 2 == 0 ? s1 : s2);
}
cout << endl;


if (s2 == 'z') {
s1++;
s2 = s1 + 1;
} else {
s2 ++;
}
}
}else {
cout << "No" << endl;
}
return 0;
}

F 期望 DP

赛时 wa 了一发,第一眼没看出来。

状态转移的 dag 上面不能有自环。

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
void solve() {
int A = read(), B = read();
double P = read(), Q = read();
P /= 100, Q /= 100;

std::cout << std::fixed << std::setprecision(15);


if (B == 1) {
double x = A * (P * 2 + (1 - P) * 1);
double y = A * (1.0 / (1 - Q));
cout << max(x, y);
return;
}

vector<double> dp(A + 1);
for (int i = B; i <= A; i++) {
dp[i] = max(
P * (dp[i - B] + 2) + (1 - P) * (dp[i - B] + 1),
Q * (dp[i - B + 1] + 1) + (1 - Q) * (dp[i - B] + 1)
);
// cout << dp[i] << "\n";
}

cout << dp[A] << "\n";
}

L

给出 n 个长方体,询问其是否拼接成一个 W × H × L 的立方体,没有重叠和空隙。

把原来大长方体等价代换成WHL个111的小方块,想象一下挖走一块移到另一个地方,会使中空的地方的8个顶点必然有奇数次覆盖,如果移到相邻位置,原来的点还是偶数次,但会造成新的点变成奇数次。所以所有顶点覆盖偶数次是充要条件。

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
void solve() {
int W = read(), H = read(), L = read();

int n = read();
vector<std::array<int, 6>> a(n);
for (int i = 0; i < n; i++) {
for (int j = 0; j < 6; j++) {
a[i][j] = read();
}
}

i128 V = (i128)W * H * L;
for (auto c : a) {
i128 v = (i128)(c[3] - c[0]) * (c[4] - c[1]) * (c[5] - c[2]);
V -= v;
}

if (V) {
puts("No");
return;
}

std::map<std::array<int, 3>, int> f;

a.push_back({0, 0, 0, W, H, L});

for (auto c : a) {
for (int x : {c[0], c[3]}) {
for (int y : {c[1], c[4]}) {
for (int z : {c[2], c[5]}) {
++f[{x, y, z}];
}
}
}
}

for (auto [x, y] : f) {
if (y & 1) {
puts("No");
return;
}
}

puts("Yes");
}

E 容斥、数论

输入 $x, k$,求大小为 $k$ 的 distinct 集合个数,其中所有数的 $\gcd + \operatorname{lcm} = x$。

设 $\gcd = G$,$\operatorname{lcm} = A \times G$

那么 $(1 + A) \times G = x$,故我们可以对 $x$ 枚举因子得到 $G$

把集合中每个数除掉 $G$,那么限制就转化为 $gcd = 1$, $lcm = A$

设 $A = \prod_{j = 1}^m{p_j^{b_j}}$, $a_i^{\prime} = \prod_{j = 1}^m{p_j^{c_{ij}}}$

限制转化为 $2m$ 条约束 $\min{c_{ij}} = 0, \max{c_{ij}} = b_j$

假设不考虑约束(也就是 $\min$ 不一定取到 $0$,$\max$ 不一定取 $b_j$),一共有 $B = \prod_{j = 1}^m{(b_j - 0 + 1)}$ 个数,在其中选 $k$ 个就是 $\binom{B}{k}$

考虑上约束,我们可以使用容斥原理:两个约束任意满不满足 - 约束1一定不满足 - 约束2一定不满足 + 约束12都一定不满足

假设约束 1 一定不满足,则 $\min$ 一定不为 $0$,可选的数变为 $B^{\prime} = \prod_{j = 1}^m{(b_j - 1 + 1)}$,在其中选 $k$ 个就是 $\binom{B^{\prime}}{k}$

约束 2 一定不满足也类似。

我们可以枚举 $2m$ 个约束分别有没有满足来进行容斥。

总的时间复杂度为 $O(Fm2^{2m})$

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
void solve() {
int x = read(), k = read();

Z ans = 0;

auto calc = [&](int G) {
int A = x / G - 1;
if (A == 0) return;

vector<int> p, e;
for (int i = 2; i * i <= A; i++) {
if (A % i != 0) continue;

e.eb(0);
while (A % i == 0) {
A /= i;
e.back()++;
}
p.eb(i);
}
if (A > 1) {
e.eb(1);
p.eb(A);
}

int m = p.size();
for (int s = 0; s < (1 << m); s++) {
for (int t = 0; t < (1 << m); t++) {
int res = 1;
int coef = 1;

for (int i = 0; i < m; i++) {
int v = e[i] + 1;

if (s >> i & 1) {
coef *= -1;
v--;
}
if (t >> i & 1) {
coef *= -1;
v--;
}

res *= v;
}

ans += comb.binom(res, k) * coef;
}
}
};

for (int G = 1; G * G <= x; G++) {
if (x % G != 0) continue;

calc(G);
if (G * G != x) {
calc(x / G);
}
}

cout << ans;
}
Author

TosakaUCW

Posted on

2024-10-15

Updated on

2024-10-18

Licensed under

Comments