VP 2024 女生赛

2024 China Collegiate Programming Contest (CCPC) Female Onsite (2024年中国大学生程序设计竞赛女生专场)

Problems AC Note
A. Box 签到
B. Aho-Corasick Automaton
C. CCPC 签到
D. Excellent Splitting
E. Centroid Tree 并查集
F. Perfect Square 数学、背包
G. Increasing Sequence 拆数位
H. Square Root 签到
I. String Duplication SAM
J. Sum of Squares of GCDs
K. Xiao Kai’s Dream of Provincial Scholarship 模拟
L. Puzzle 思维
M. Covering a Tree 贪心

A. Box

Code >folded
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 z0 = read();
int h = read();
int u0 = read();
int v0 = read();
int u1 = read();
int v1 = read();

// u0 v0 z0
// u1 v1 z0
// u0 v1 z0
// u1 v0 z0
if (u0 > u1) {
swap(u0, u1);
}
if (v0 > v1) {
swap(v0, v1);
}

for (int q = read(); q--; ) {
int x = read();
int y = read();
int z = read();

if (x < u0 or x > u1 or y < v0 or y > v1 or z > z0 + h or z < z0) {
puts("NO");
} else {
puts("YES");
}
}
}

C. CCPC

Code >folded
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void solve() {
string s;
cin >> s;

int a = 0, b = 0;
for (auto ch : s) {
if (ch == 'C') a++;
if (ch == 'P') b++;
}
if (a < 3 or b < 1) {
puts("0");
return;
}
cout << min((a - 1) / 2, b) << '\n';
}

E. Centroid Tree

Code >folded
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
void solve() {
int n = read();
DSU dsu(n + 1);

vector<int> c(n + 1);
vector<vector<int>> e(n + 1);
for (int i = 1; i <= n; i++) {
c[i] = read();
for (int j = 1; j <= c[i]; j++) {
int x = read();
e[i].eb(x);
}
}

vector<int> par(n + 1);
for (int i = n; i >= 1; i--) {
for (auto x : e[i]) {
int now = dsu.find(x);
par[now] = i;
dsu.merge(i, now);
}
}

for (int i = 2; i <= n; i++) {
cout << par[i] << " " << i << '\n';
}
}

F. Perfect Square

给出一个长度为 $n$ 的正整数序列 $a$,求所有序列 $d$ 满足 $d_i|a_i$ 且 $d_i$ 的乘积为完全平方数的乘积开方之和。

$1 \le n \le 10^6, 1 \le a_i \le 10^6$

每个质因子的贡献是独立的。

相当于 $n$ 个物品,每个物品可以拿最多 $c_i$ 个,问总共取偶数个的方案里,$p ^ \frac{取的数量}{2}$ 的和。

Code
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
void solve() {
int n = read();
vector<vector<int>> g(1e6);
for (int i = 1; i <= n; i++) {
for (int x = read(); x != 1; ) {
int p = minp[x];
int c = 0;

while (x % p == 0) {
x /= p;
c++;
}

g[p].eb(c);
}
}

auto go = [&](vector<int> c, int p) -> Z {
int m = c.size();
if (m == 0) return Z(1);

int sum = std::accumulate(c.begin(), c.end(), 0);
vector<int> pow(sum + 1);
pow[0] = 1;
for (int i = 1; i <= sum; i++) {
pow[i] = pow[i - 1] * p;
}

vector<Z> dp(2);
dp[0] = 1;

for (int i = 0; i < m; i++) {
vector<Z> ndp(2);
for (int j = 0; j <= 1; j++) {
for (int k = 0; k <= c[i]; k++) {
int nj = (j + k) % 2;
ndp[nj] += dp[j] * pow[(j + k) / 2];
}
}
dp = std::move(ndp);
}

return dp[0];
};

Z ans = 1;
for (auto p : primes) {
ans *= go(g[p], p);
}
cout << ans << '\n';
}

G. Increasing Sequence

给定长度为 $n$ 的数列 $a$ 和整数 $k$,询问有多少整数满足 $x \in [0, k]$,且有 $a_1 \oplus x, a_2 \oplus x, \cdots , a_n \oplus x$ 是单调不降序列。

$1 \le n \le 2 \cdot 10^5, 1 \le k \le 10^{18}$

对于相邻两项 $a_i \oplus x \le a_{i + 1} \oplus x$,从高到低找到 $a_i$ 和 $a_{i + 1}$ 第一个不同的位。在这一位上,如果 $a_{i}$ 为 $0$,$a_{i + 1}$ 为 $1$,则 $x$ 必须为 $0$;反之 $x$ 必须为 $1$。

那么现在有一些限制,问你有多少个 $x \in [0, k]$,就是数位 DP 的板子。

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

vector<int> a(n + 1);
vector<int> b(65, -1);
// vector<int> c(65, -1);
for (int i = 1; i <= n; i++) {
a[i] = read();

}

for (int i = 1; i < n; i++) {
for (int j = 60; j >= 0; j--) {
int x = a[i] >> j & 1;
int y = a[i + 1] >> j & 1;

if (x != y) {
int now = x;
if (b[j] != -1 and b[j] != x) {
puts("0");
return;
}
b[j] = x;
break;
}
}
}

// for (int i = 0; i < 60; i++) {
// b[i] = b[i] == -1 ? 2 : 1;
// cerr << b[i] << " ";
// }

// int ans = 1;

// for (int i = 60; i >= 0; i--) {
// int x = k >> i & 1;
// if (x) {
// cout << "i: " << i << '\n';
// int res = 1;
// // i - b[i]
// if (i - 1 >= 0)
// res *= 1LL << (i - b[i - 1]);
// // cout << "res: " << res << '\n';
// ans += res;
// }
// }

vector dp(65, vector<int>(2, -1));

auto dfs = [&](auto&& self, int now, bool f) -> int {
if (now == -1) return 1;
if (~dp[now][f]) return dp[now][f];

int res = 0;
int lim = f ? (k >> now & 1) : 1;
for (int j = 0; j <= lim; j++) {
if (b[now] == -1 or b[now] == j) {
res += self(self, now - 1, f && (j == lim));
}
}

dp[now][f] = res;
return res;
};

int ans = dfs(dfs, 60, 1);

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

H. Square Root

Code >folded
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
void solve() {
string s;
cin >> s;

int n = s.size();
for (int i = 0; i + 1 < n; i++) {
if (i - 1 < 0 or i + 1 >= n) continue;
if (s[i - 1] == '1' and s[i] == '1' and s[i + 1] == '1')
s[i] = '0';
}
double ans = 0;
s += '0';
int cnt = 0;
for (int i = 0; i <= n; i++) {
if (s[i] == '0') {
ans += pow(cnt, 0.5);
// cout << cnt << '\n';
cnt = 0;
} else {
cnt++;
}
}
printf("%.10f", ans);
}

I. String Duplication

给定一个字符串 $S$,求 $S$ 复制 $m$ 次拼接起来的的字符串的本质不同子串个数。

$1 \le |S| \le 3 \times 10^5, 1 \le m \le 10^9$

打表可以直接发现规律,以下是参考官方题解的证明:

令函数 $f(S, m)$ 表示 $S$ 复制 $m$ 次的本质不同子串个数。

当 $m \ge 3$ 时,如果一个串是 $SSS$ 的子串,而且不是 $SS$ 的子串,那么可以被写成 $x[S]y$ 的形式($x$ 为 $S$ 的非空后缀,$y$ 为 $S$ 的非空前缀$。

同理一个串是 $SSSS$ 的子串,而且不是 $SSS$ 的子串,那么可以被写成 $x[SS]y$ 的形式

显然 $x[S]y$ 的数量等于 $x[SS]y$ 的数量。并且这个数量等于 $f(S, 3) - f(S, 2)$。

等差数列求和就做完了。求本质不同子串直接 sam 即可。

Code
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 n = read();
int m = read();

SAM sam;

string s;
cin >> s;

vector<Z> ans(3);
int p = 1;
for (int k = 0; k < 3; k++) {
for (auto c : s) {
p = sam.extend(p, c - 'a');
}
for (int i = 2; i < sam.size(); i++) {
ans[k] += sam.len(i) - sam.len(sam.link(i));
}
}

if (m <= 2) {
cout << ans[m - 1] << '\n';
} else {
cout << (ans[2] - ans[1]) * (m - 2) + ans[1] << '\n';
}
}

J. Sum of Squares of GCDs

Code >folded
1

M. Covering a Tree

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

vector<int> par(n + 1);
vector<vector<int>> g(n + 1);
for (int i = 2; i <= n; i++) {
int p = read();
par[i] = p;
g[p].eb(i);
}

vector<int> dep(n + 1);

vector<pii> leaf;

auto dfs = [&](auto&& self, int u) -> void {
for (int v : g[u]) {
dep[v] = dep[u] + 1;
self(self, v);
}
if (g[u].empty()) {
leaf.eb(dep[u], u);
}
};
dfs(dfs, 1);

std::sort(leaf.begin(), leaf.end());

// for (auto [d, u] : leaf) {
// cerr << d << " " << u << "\n";
// }

int ans = 0;
vector<int> vis(n + 1);
for (auto [_, u] : leaf) {
int res = 0;
for (int v = u; par[v] != 0; v = par[v]) {
// v -> par[v]
res++;
// cout << "vis" << v << " " << par[v] << "\n";
if (vis[v]) break;
vis[v] = 1;
ans = max(ans, res);
}
}

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

TosakaUCW

Posted on

2024-12-10

Updated on

2024-12-10

Licensed under

Comments