P9731 [CEOI2023] Balance 题解

Description

由于黑客对评测机的攻击,组委会决定重测所有提交记录。

NN 台评测机,TT 个题目(编号为 1,2,,T1, 2, \cdots, T)。组委会已经确定,每台评测机要评测哪些提交(数目相同,都是 SS 个提交,保证 SS22 的整数次幂)。在接下来的 SS 分钟内,每分钟每台评测机会评测一个提交。

每个提交都会提交至某个题目。由于存数据的机器太脆弱了,所以要求,对于所有题目和任意两个时刻,在这两个时刻,这个题的被评测的提交的数量之差不超过 11

请构造一组方案,使得满足上面的条件。

保证存在正整数 kk 使得 S=2kS = 2 ^ k1N,S,T1051 \le N, S, T \le 10 ^ 5NS5×105NS \le 5 \times 10 ^ 5

Solution

考虑 S=2S=2 的时候怎么做。

这里相当于要选择某些评测机并交换他们的两个题目,使得每个题目在第一列和第二列出现次数相差不超过 11

那么如果把每个评测机的两个题目连一条无向边,题目等价于给每个无向边定向,使得每个点入度和出度相差不超过 11,这就是个欧拉回路的经典题了,只要建一个超级源点并与所有奇度点连边,然后对于每个连通块跑欧拉回路,每条边在欧拉回路里的方向就是最终的方向。


然后是 S>2S>2 的做法。

考虑把 SS 平分为两段,先让每个点在这两段里出现的次数相差不超过 11,然后分治处理,则最后的答案一定满足条件。

建图就考虑对于每行,ai,ja_{i,j}aj+S2a_{j+\frac{S}{2}} 连边,跑上面那个算法,虽然方案不一定是刚好 swap ai,ja_{i,j}aj+S2a_{j+\frac{S}{2}},但是这样一定能构造出来。

时间复杂度:O((NS+T)log2S)O\left((NS+T)\log^2 S\right),可以用 unordered_map 优化掉一个 log\log

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
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
#include <bits/stdc++.h>

// #define int int64_t

const int kMaxN = 1e5 + 5, kMaxS = 5e5 + 5;

struct my_hash {
static uint64_t splitmix64(uint64_t x) {
x += 0x9e3779b97f4a7c15;
x = (x ^ (x >> 30)) * 0xbf58476d1ce4e5b9;
x = (x ^ (x >> 27)) * 0x94d049bb133111eb;
return x ^ (x >> 31);
}

size_t operator()(uint64_t x) const {
static const uint64_t FIXED_RANDOM =
std::chrono::steady_clock::now().time_since_epoch().count();
return splitmix64(x + FIXED_RANDOM);
}

size_t operator()(std::pair<uint64_t, uint64_t> x) const {
static const uint64_t FIXED_RANDOM =
std::chrono::steady_clock::now().time_since_epoch().count();
return splitmix64(x.first + FIXED_RANDOM) ^
(splitmix64(x.second + FIXED_RANDOM) >> 1);
}
};

int n, m, t;
int cur[kMaxN];
bool vis[kMaxS], vs[kMaxN];
std::vector<int> a[kMaxN], vec;
std::vector<std::pair<int, int>> G[kMaxN];

void dfs(int u) {
vs[u] = 1;
for (int i = cur[u]; i < (int)G[u].size(); i = cur[u]) {
cur[u] = i + 1;
int v = G[u][i].first, id = G[u][i].second;
if (vis[id]) continue;
vis[id] = 1, dfs(v);
}
vec.emplace_back(u);
}

void solve(int l, int r) {
if (l == r) return;
for (int i = 0; i <= t; ++i)
G[i].clear(), cur[i] = vs[i] = 0;
int mid = (l + r) >> 1, len = r - l + 1, id = 0;
for (int i = 1; i <= n; ++i) {
for (int j = l; j <= mid; ++j) {
vis[++id] = 0;
G[a[i][j]].emplace_back(a[i][j + len / 2], id);
G[a[i][j + len / 2]].emplace_back(a[i][j], id);
}
}
int rt = 1;
for (int i = 1; i <= t; ++i) {
if (G[i].size() & 1) {
rt = 0, vis[++id] = 0;
G[rt].emplace_back(i, id), G[i].emplace_back(rt, id);
}
}
cur[rt] = 0;
std::unordered_map<std::pair<int, int>, int, my_hash> mp;
for (int i = rt; i <= t; ++i) {
if (!vs[i]) {
vec.clear();
dfs(i);
for (int j = 0; j + 1 < (int)vec.size(); ++j)
++mp[{vec[j], vec[j + 1]}];
}
}
for (int i = 1; i <= n; ++i) {
for (int j = l; j <= mid; ++j) {
if (!mp[{a[i][j], a[i][j + len / 2]}]) {
std::swap(a[i][j], a[i][j + len / 2]);
}
--mp[{a[i][j], a[i][j + len / 2]}];
}
}
solve(l, mid), solve(mid + 1, r);
}

void dickdreamer() {
std::cin >> n >> m >> t;
for (int i = 1; i <= n; ++i) {
a[i].resize(m);
for (auto &x : a[i]) std::cin >> x;
}
solve(0, m - 1);
for (int i = 1; i <= n; ++i) {
for (auto x : a[i]) std::cout << x << ' ';
std::cout << '\n';
}
}

int32_t main() {
#ifdef ORZXKR
freopen("in.txt", "r", stdin);
freopen("out.txt", "w", stdout);
#endif
std::ios::sync_with_stdio(0), std::cin.tie(0), std::cout.tie(0);
int T = 1;
// std::cin >> T;
while (T--) dickdreamer();
// std::cerr << 1.0 * clock() / CLOCKS_PER_SEC << "s\n";
return 0;
}

P9731 [CEOI2023] Balance 题解
https://sobaliuziao.github.io/2024/04/03/post/f95424f2.html
作者
Egg_laying_master
发布于
2024年4月3日
许可协议