CEOI2023D1T3(LOJ4019/LG9731) Brought Down the Grading Server(Balance)? (分治+欧拉回路)
因为我们有 ,所以我们先考虑 即 的时候应该怎么做。
发现如果我们对于每一个核心从 向 连一条无向边,如果我们把 「不交换 」 看成将这条边定向为 ,否则如果「交换 」看成将这条边定向为 ,我们会发现对于一个数 在左边出现的次数相当于 的出度, 在右边出现的次数相当于 的入度,我们要找到一种定向方法使得这个图上每个点的入度和出度相差不超过 。
于是我们想到了欧拉回路,欧拉回路上每个点的出度和入度相等(因为每个点出去一次进来一次),但问题是我们的这个无向图不一定有欧拉回路,且我们只需要入度和出度相差不超过 。
考虑到一个无向图有欧拉回路当且仅当这个图上的每一个点的度数都为偶数,于是我们想到建立一个源点,从这个源点向每个度数为奇数的点连一条边,然后在这个新的图上跑欧拉回路,最后只用关注那些原先就在这个图上的边,这样就可以保证每个点的出度和入度相差不超过 了。
接下来考虑 应该怎么做,由于 ,我们可以先将前 个评测看成一个整体,将后 个评测看成一个整体,我们要使得对于任意一个数 ,其在前 个中出现的次数与其在后 个评测中出现的次数相等,于是我们发现只需要将 与 连边,然后跑一遍欧拉回路,然后分治地处理 与 即可。
因为我们可能会建重边,所以可以在每条边中插入一个点,以记录重边的编号(当然可能不记录也是对的,但是我没验证过)。
时间复杂度 。
const int MAXN = 5e6 + 5;
int ecnt, n, s, t, d[MAXN], hd[MAXN], st[MAXN], top;
vector<vector<int>> a;
struct _node {
int v, ex, rev;
};
vector<_node> G[MAXN];
bool vis[MAXN];
struct _edge {
int u, v, i, j;
bool operator < (const _edge b) const {
return u == b.u ? v < b.v : u < b.u;
}
};
void Hierholzer(int x) {
vis[x] = true;
for (int &i = hd[x]; i < (int)G[x].size();) {
if (G[x][i].ex) {
G[x][i].ex = G[G[x][i].v][G[x][i].rev].ex = 0;
i++;
Hierholzer(G[x][i - 1].v);
} else ++i;
}
st[++top] = x;
}
void add(int x, int y) {
G[x].push_back({y, 1, G[y].size()});
G[y].push_back({x, 1, G[x].size() - 1});
}
void solve(int l, int r) {
int len = (r - l + 1);
ecnt = top = 0;
int x = t;
unordered_map<int, pair<int, int>> emap;
for (int i = 0; i < len / 2; ++i) {
for (int j = 1; j <= n; ++j) {
const int u = a[j][l + i], v = a[j][r - i];
if (u == v) continue;
add(u, ++x); add(v, x);
emap[x] = make_pair(i, j);
d[u]++; d[v]++; d[x] += 2;
}
}
for (int i = 1; i <= t; ++i) {
if (d[i] % 2 == 1) {
add(0, ++x);
add(i, x);
}
}
for (int i = 0; i <= t; ++i) {
if (vis[i]) continue;
top = 0;
Hierholzer(i);
reverse(st + 1, st + top + 1);
for (int j = 3; j <= top; j += 2) {
if (!st[j] || !st[j - 2] || st[j] == st[j - 2]) continue;
const auto _ = emap[st[j - 1]];
int &u = a[_.second][l + _.first], &v = a[_.second][r - _.first];
if (u != st[j - 2]) {
swap(u, v);
}
}
}
for (int i = 0; i <= t + x; ++i) G[i].clear(), vis[i] = false, d[i] = hd[i] = 0;
if (len != 2) {
solve(l, mid); solve(mid + 1, r);
}
}
void work() {
cin >> n >> s >> t;
a.resize(n + 1);
for (int i = 1; i <= n; ++i) {
a[i].resize(s + 1);
for (int j = 1; j <= s; ++j)
cin >> a[i][j];
}
solve(1, s);
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= s; ++j) cout << a[i][j] << ' ';
cout << endl;
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效