CCPC2023 哈尔滨站
# CCPC2023 哈尔滨站
The 9th CCPC (Harbin) Onsite(The 2nd Universal Cup. Stage 10: Harbin) (opens new window)
# B - Memory
# Question
给定一个数组 求每个 的正负性
# Solution
很显然,如果用 double 暴力枚举的话会产生精度问题,这里我发现 也就是说,我们考虑模拟二进制加法,对于一个数 最多只会影响到 这个区间内的正负,我们只需要记录下 这个区间的值,当这个值为 的时候,才会考虑 这个部分的正负
# Code
#include <bits/stdc++.h>
int main() {
freopen ("B.in", "r", stdin);
std::ios::sync_with_stdio(false);
std::cin.tie(0); std::cout.tie(0);
int n; std::cin >> n;
int flg = 0;
size_t now_up = 0, now_dn = 0;
for (int i = 1; i <= n; i++) {
int x; std::cin >> x;
if (x > 0) now_up += x;
if (x < 0) now_dn += -x;
if (now_up == now_dn) {
if (flg == 0) std::cout << '0';
if (flg == 1) std::cout << '+';
if (flg == -1) std::cout << '-';
}
if (now_up > now_dn)
std::cout << '+';
if (now_dn > now_up)
std::cout << '-';
if ((now_dn & 1) ^ (now_up & 1)) {
if (now_dn & 1) flg = -1;
if (now_up & 1) flg = 1;
}
now_dn >>= 1; now_up >>= 1;
}
std::cout << std::endl;
return 0;
}
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
# D - A Simple MST Problem (数论,最小生成树,银牌题)
# Question
定义 为 的质因子集合的大小,多次询问,每次询问给定一个区间 ,只考虑 中的值, 建边的代价是 ,求最小生成树的边权和
# Solution
很显然有
根据贪心的想法,我们需要找边权非常小的点,那么就是 的点,然后考虑其他的 向他连边,边权就是
然后考虑边权为 的情况,如果 向 连边,且 的质因子集合是 的质因子集合的子集,则边权为
由于一个 可能对应着不同 我们对于每个 ,只需要找到左边和右边离它最近的 建边即可
这里当时我不知道怎么处理,然后看了别人代码,发现可以记录下质因子集合的乘积,因为质因子集合和乘积是一一对应的,我们记在我之前的离我最近的质因子集合的乘积的下标为 ,那么对于每个数 ,需要枚举 的因子 ,然后求 就是在 左边的最近的满足条件的 ,我们记为 ,同理,我们能得在 右边的
我们只需要对 连 的边,对 连 的边,然后刷 MST 即可
如果区间中不存在 的 ,那么区间很小,直接 暴力建边即可
# Code
#include <bits/stdc++.h>
struct DSU {
std::vector<int> f;
std::vector<int> size;
DSU(int n) : f(n), size(n) {
std::iota(f.begin(), f.end(), 0);
std::fill(size.begin(), size.end(), 1);
}
int find(int x) { // 路径压缩
while (x != f[x])
x = f[x] = f[f[x]];
return x;
}
void Union(int x, int y) {
if (find(x) == find(y))
return;
if (size[x] < size[y]) // 按秩合并
std::swap(x, y);
size[find(x)] += size[find(y)];
f[find(y)] = find(x);
}
};
constexpr int N = 1e6 + 5;
constexpr int INF = 0x3f3f3f3f;
int P[N], w[N], proP[N], v[N], tot;
int L[N], R[N], cnt[N];
void init() {
v[1] = 1;
std::fill(proP, proP + N, 1);
for (int i = 2; i < N; i++) {
if (v[i] == 0) {
P[++tot] = i;
v[i] = i; w[i] = 1;
proP[i] = i;
}
for (int j = 1; j <= tot && i * P[j] < N; j++) {
v[i * P[j]] = P[j];
if (i % P[j] == 0) {
w[i * P[j]] = w[i];
proP[i * P[j]] = proP[i];
break;
}
w[i * P[j]] = w[i] + 1;
proP[i * P[j]] = proP[i] * P[j];
}
}
auto find_div = [&] (int x) {
std::vector<int> res(1, 1);
while (x > 1) {
int d = v[x];
x /= d;
for (int i = res.size() - 1; i >= 0; i--)
res.push_back(res[i] * d);
}
return res;
};
for (int i = 1; i < N; i++) {
auto divs = find_div(proP[i]);
for (int d : divs)
L[i] = std::max(L[i], cnt[d]);
cnt[proP[i]] = i;
}
std::fill(cnt, cnt + N, INF);
std::fill(R, R + N, INF);
for (int i = N - 1; i; i--) {
auto divs = find_div(proP[i]);
for (int d : divs)
R[i] = std::min(R[i], cnt[d]);
cnt[proP[i]] = i;
}
}
void cpSort(std::vector<std::array<int, 3>> &a) {
int M = 0;
for (const auto &[w, x, y] : a) M = std::max(M, w);
std::vector<std::vector<int>> cnt(M + 1);
for (int i = 0; i < a.size(); i++)
cnt[a[i][0]].push_back(i);
std::vector<std::array<int, 3>> res;
for (int x = 0; x <= M; x++)
for (auto i : cnt[x])
res.push_back(a[i]);
a = res;
}
int mst(std::vector<std::array<int, 3>> &e, int l, int r) {
DSU dsu(r - l + 1);
cpSort(e);
int ret = 0;
for (const auto &[w, x, y] : e) {
if (dsu.find(x - l) != dsu.find(y - l)) {
dsu.Union(x - l, y - l);
ret += w;
}
}
return ret;
}
int edge(int x, int y) {
return w[x] + w[y] - w[std::gcd(x, y)];
}
void solve() {
int l, r; std::cin >> l >> r;
int ans = std::accumulate(w + l, w + r + 1, 0);
if (l == 1) {
std::cout << ans << '\n';
return;
}
std::vector<std::array<int, 3>> e;
int prime = 0;
for (int x = l; x <= r; x++) {
if (w[x] == 1)
prime = x;
}
if (prime == 0) {// 区间内没有 w[x]=1 的数
for (int i = l; i < r; i++)
for (int j = i + 1; j <= r; j++)
e.push_back({edge(i, j), i, j});
ans = mst(e, l, r);
std::cout << ans << '\n';
return ;
}
for (int x = l; x <= r; x++) {
if (L[x] >= l)
e.push_back({w[x], x, L[x]});
if (R[x] <= r)
e.push_back({w[x], x, R[x]});
if (prime != x)
e.push_back({edge(x, prime), x, prime});
}
ans = mst(e, l, r);
std::cout << ans << '\n';
}
int main() {
freopen ("D.in", "r", stdin);
std::ios::sync_with_stdio(false);
std::cin.tie(NULL); std::cout.tie(NULL);
init();
int _; std::cin >> _;
while (_--) solve();
return 0;
}
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
156
157
158
159
160
161
162
163
164
165
166
167
# Summary
当值域特别小时,可以考虑用桶排序
这里在求一个数的因子的时候,用了一种比较特殊的办法,用欧拉筛维护了一个 数组, 表示 最小的质因子,假设现在已经求得的因子集合为 ,现在这个数为 ,那么对于 的最大因子 , 中的每个数 同样也是 的因子
auto find_div = [&] (int x) {
std::vector<int> res(1, 1);
while (x > 1) {
int d = v[x];
x /= d;
for (int i = res.size() - 1; i >= 0; i--)
res.push_back(res[i] * d);
}
return res;
};
2
3
4
5
6
7
8
9
10
时间复杂度要优于 的预处理的
# G - The Only Way to the Destination (图论, 铜牌题)
# Quesiton
给定一个 的方格,方格中有一些 个墙,每个墙描述为 ,需要判断空白位置是否只存在一条简单路径
# Solution
由于 很大,但分析发现,当 时,显然为 NO
,所以本质上 和 是同阶的。
假设我们对每个空格子都上下左右建边,只需要判断是否存在环即可
但是 的数量级太大,考虑如何优化,其实我们只需要把在同一个 的,连续的一段空节点看成一个节点,然后对于相邻的 之间建边即可
# Code
#include <bits/stdc++.h>
struct Wall {
int x1, x2, y, id;
};
int main() {
freopen ("G.in", "r", stdin);
std::ios::sync_with_stdio(false);
std::cin.tie(NULL); std::cout.tie(NULL);
int n, m, k; std::cin >> n >> m >> k;
std::vector<Wall> walls(k);
for (int i = 0; i < k; i++) {
std::cin >> walls[i].x1 >> walls[i].x2 >> walls[i].y;
}
std::sort(walls.begin(), walls.end(), [&](const Wall &a, const Wall &b) {
return a.y < b.y || (a.y == b.y && a.x1 < b.x1);
});
if (n == 1) {
int l = 0, r = k - 1;
while (l < k && walls[l].y == l + 1) l += 1;
while (r >= 0 && walls[r].y == m - (k - 1 - r)) r -= 1;
std::cout << (l > r ? "YES" : "NO") << '\n';
return 0;
}
int cnt = 0, pos = 0;
std::vector<std::vector<int>> g(10 * k);
auto add_e = [&] (int u, int v) {
g[u].push_back(v); g[v].push_back(u);
};
std::vector<Wall> v, pre_v;
for (int i = 1; i <= m; i++) { // 枚举列
pre_v = v; v.clear();
while (pos < k && walls[pos].y == i) {
if (pos == 0 || walls[pos].y != walls[pos - 1].y) {
if (walls[pos].x1 > 1)
v.push_back({1, walls[pos].x1 - 1, i, ++cnt});
}
else {
if (walls[pos - 1].x2 + 1 <= walls[pos].x1 - 1)
v.push_back({walls[pos - 1].x2 + 1, walls[pos].x1 - 1, i, ++cnt});
}
pos += 1;
}
if (pos == 0 || walls[pos - 1].y != i) {
if (pre_v.size() == 1 && pre_v.back().x1 == 1 && pre_v.back().x2 == n) {
std::cout << "NO\n";
return 0;
}
v.push_back({1, n, i, ++cnt});
}
else if (walls[pos - 1].x2 < n) {
v.push_back({walls[pos - 1].x2 + 1, n, i, ++cnt});
}
for (int pre_j = 0, j = 0; pre_j < pre_v.size(); pre_j++) {
while (j < v.size() && v[j].x2 < pre_v[pre_j].x1) j += 1;
while (j < v.size() && v[j].x1 <= pre_v[pre_j].x2) { // 相交
int L = std::max(v[j].x1, pre_v[pre_j].x1), R = std::min(v[j].x2, pre_v[pre_j].x2);
if (R ^ L) {
std::cout << "NO\n";
return 0;
}
add_e(v[j].id, pre_v[pre_j].id);
if (v[j].x2 > pre_v[pre_j].x2) break;
j += 1;
}
}
}
g.resize(cnt + 1);
if (cnt == 1) {
std::cout << "YES" << "\n";
return 0;
}
std::vector<int> du(cnt + 1, 0);
int num = 0; std::queue<int> q;
for (int i = 1; i <= cnt; i++) du[i] = g[i].size();
for (int i = 1; i <= cnt; i++) if (du[i] == 1)
q.push(i), num += 1;
while (!q.empty()) {
int u = q.front(); q.pop();
for (auto v : g[u]) {
if (--du[v] == 1) {
q.push(v); num += 1;
}
}
}
std::cout << (num == cnt ? "YES" : "NO") << '\n';
return 0;
}
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
# H - Energy Distribution (高斯消元, 银牌题)
# Question
给出一个 的矩阵,限制为 ,求
的最大值
# Solution
枚举 为 的项,然后使用拉格朗日乘数法,求最值
# Code
#include <bits/stdc++.h>
using ld = double;
constexpr ld eps = 1e-9;
int sgn(const ld &a) {
if (a < -eps)
return -1;
return (a > eps);
}
std::string gauss(std::vector<std::vector<ld>> &a) { // 传入增广矩阵
int n = a.size();
int c = 0, r = 0;
for (c = 0, r = 0; c < n; c++) {
int tmp = r;
for (int i = r; i < n; i++)
if (sgn(a[i][c]))
tmp = i;
if (sgn(a[tmp][c]) == 0)
continue;
std::swap(a[tmp], a[r]);
for (int i = n; i >= c; i--)
a[r][i] /= a[r][c];
for (int i = r + 1; i < n; i++)
if (sgn(a[i][c]))
for (int j = n; j >= c; j--)
a[i][j] -= a[r][j] * a[i][c];
r += 1;
}
if (r < n) {
for (int i = r; i < n; i++)
if (sgn(a[i][n]))
return "NoSolution";
return "InfSolution";
}
for (int i = n - 1; i >= 0; i--)
for (int j = i + 1; j < n; j++)
a[i][n] -= a[j][n] * a[i][j];
return "OK";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr); std::cout.tie(nullptr);
int n; std::cin >> n;
std::vector w(n, std::vector<int>(n , 0));
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
std::cin >> w[i][j];
ld ans = 0;
for (int S = 1; S < (1 << n); S++) {
int m = __builtin_popcount((S));
if (m <= 1)
continue;
std::vector a(m, std::vector<ld>(m + 1, 0));
std::vector<int> b;
for (int T = S; T ; T -= T & -T) {
b.push_back(std::__lg(T & -T));
}
for (int i = 0; i <= m; i++)
a[0][i] = 1;
for (int j = 1; j < m; j++) {
for (int k = 0; k < m; k++)
a[j][k] = w[b[j]][b[k]] - w[b[j - 1]][b[k]];
}
auto res = gauss(a);
if (res == "OK") {
ld now = 0;
for (int i = 0; i < m; i++)
if (sgn(a[i][m]) == -1)
now = -1e9;
for (int i = 0; i < m; i ++)
for (int j = i + 1; j < m; j++)
now += a[i][m] * a[j][m] * w[b[i]][b[j]];
ans = std::max(ans, now);
}
}
std::cout << std::fixed << std::setprecision(10) << ans << '\n';
return 0;
}
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
# J - Game on a Forest (SG函数,铜牌题)
# Question
给出一个森林,Plover和Georgia轮流对图进行操作,Georgia先手,操作是两种类型之一
- 选择图中的一条边,然后移除它。
- 选择图中的一个节点,然后移除该节点以及与其相连的边。
第一个无法操作的人失败,求 Georgia 有多少种不同的必胜的第一次操作
# Solution
博弈问题考虑 SG 函数,通过归纳我们可以发现,对于一颗树来说,节点个数为奇数, 为 ,节点个数为偶数时, 为
总体的 函数就是所有树的异或和,我们只需要枚举第一次操作,然后判断第一次操作后剩下的子图的异或和是否为 即可判断是否是 Georgia 的必胜局面
# Code
#include <bits/stdc++.h>
int main() {
freopen ("J.in", "r", stdin);
std::ios::sync_with_stdio(false);
std::cin.tie(NULL); std::cout.tie(NULL);
int n, m; std::cin >> n >> m;
std::vector<std::pair<int, int>> edges;
std::vector<std::vector<int>> g(n + 1);
for (int i = 0; i < m; i++) {
int u, v; std::cin >> u >> v;
edges.push_back({u, v});
g[u].push_back(v); g[v].push_back(u);
}
std::function<int(int)> sg = [&](int u) {
if (u == 0) return 0;
if (u & 1) return 1;
else return 2;
};
std::vector<int> from(n + 1, 0), siz(n + 1, 0);
std::function<void(int, int, int)> dfs = [&](int u, int fa, int frm) {
from[u] = frm;
siz[u] = 1;
for (int v : g[u]) {
if (v == fa) continue;
dfs(v, u, frm);
siz[u] += siz[v];
}
};
int cnt = 0, SG = 0;
for (int i = 1; i <= n; i++) if (siz[i] == 0) {
dfs(i, 0, i); cnt += 1;
SG = SG ^ sg(siz[i]);
}
int ans = 0;
for (int i = 1; i <= n; i++) {
int now_sg = SG ^ sg(siz[from[i]]);
for (auto v : g[i]) {
if (siz[v] > siz[i]) continue;
now_sg = now_sg ^ sg(siz[v]);
}
now_sg = now_sg ^ sg(siz[from[i]] - siz[i]);
if (now_sg == 0) ans += 1;
}
for (auto [u, v] : edges) {
int now_sg = SG ^ sg(siz[from[u]]);
int siz_ = std::min(siz[u], siz[v]);
now_sg ^= sg(siz_); now_sg ^= sg(siz[from[u]] - siz_);
if (now_sg == 0) ans += 1;
}
std::cout << ans << '\n';
return 0;
}
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
# L- Palm Island
# Code
#include<bits/stdc++.h>
using namespace std;
typedef int ll;
ll Tex, n, x;
vector<ll> a, b;
void f(vector<ll> & qwq, vector<ll> & p){
for(auto &it : p){
qwq.push_back(it);
}
p.clear();
}
void AC(){
cin >> n;
a.clear(); b.clear();
a.push_back(0);
b.push_back(0);
for(int i = 1; i <= n; i ++){
cin >> x;
a.push_back(x);
}
for(int i = 1; i <= n; i ++){
cin >> x;
b.push_back(x);
}
vector<ll> q1, q2, q3, q4;
for(int i = n; i >= 1; i --){
if(b[i] == a[i]) continue;
ll idx;
for(int j = 1; j < i; j ++){
if(a[j] == b[i]) idx = j;
}
// cout << " " << i << "\n";
q1.clear(); q2.clear(); q3.clear(); q4.clear();
q4.push_back(b[i]);
if(idx - 1 > 0){
for(int k = 1; k < idx; k ++){
q1.push_back(a[k]);
}
}
if(i - idx > 0){
for(int k = idx + 1; k <= i; k ++){
q2.push_back(a[k]);
}
}
if(n - i > 0){
for(int k = i + 1; k <= n; k ++){
q3.push_back(a[k]);
}
}
if(i == n){
if(idx > 0) cout << string(idx, '1');
a.clear(); a.push_back(0);
if(q2.size()) f(a, q2);
if(q1.size()) f(a, q1);
if(q4.size()) f(a, q4);
}
else{
if(idx - 1 > 0) cout << string(idx - 1, '1');
if(i - idx > 0) cout << string(i - idx, '2');
if(n - i + 1 > 0) cout << string(n - i + 1, '1');
a.clear(); a.push_back(0);
if(q1.size()) f(a, q1);
if(q2.size()) f(a, q2);
if(q4.size()) f(a, q4);
if(q3.size()) f(a, q3);
}
}
cout << endl;
}
int main(){
freopen ("L.in", "r", stdin);
freopen ("L.out", "w", stdout);
// ios::sync_with_stdio(false);
// cin.tie(0);cout.tie(0);
cin >> Tex;
while(Tex --) AC();
return 0;
}
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
# M - Painter
# Solution
队友写的签到
# Code
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node{
string id;
ll x1, y1, x2, y2, r;
char ch;
};
ll n;
vector<node> a;
bool check1(ll x, ll y, ll r, ll x0, ll y0){
return (x0 - x) * (x0 - x) + (y0 - y) * (y0 - y) <= r * r;
}
bool check2(ll x1, ll y1, ll x2, ll y2, ll x0, ll y0){
return (x1 <= x0) && (x0 <= x2) && (y1 <= y0) && (y0 <= y2);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i ++){
string opt;
ll x1, y1, x2, y2, r;
char ch;
cin >> opt;
if(opt == "Circle"){
cin >> x1 >> y1 >> r >> ch;
a.push_back({opt, x1, y1, 0, 0, r, ch});
}
else if(opt == "Rectangle"){
cin >> x1 >> y1 >> x2 >> y2 >> ch;
a.push_back({opt, x1, y1, x2, y2, 0, ch});
}
else{
cin >> x1 >> y1 >> x2 >> y2;
for(int j = y2; j >= y1; j --){
for(int i = x1; i <= x2; i ++){
// cout << i << " " << j << "\n";
char ans = '.';
for(int k = a.size() - 1; k >= 0; k --){
if(a[k].id == "Circle"){
if(check1(a[k].x1, a[k].y1, a[k].r, i, j)){
ans = a[k].ch;
break;
}
}
else{
if(check2(a[k].x1, a[k].y1, a[k].x2, a[k].y2, i, j)){
ans = a[k].ch;
break;
}
}
}
cout << ans;
}
cout << "\n";
}
}
}
return 0;
}
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