一个很好理解很好写的做法。显然答案的上界是 。
考虑对于一条 的路径,则要么在圆上顺时针走要么逆时针走。考虑定义一条路径的状态 :
- :即维护 顺时针走到 的路径。
- :即维护 逆时针走到 的路径。
那么可以定义一段路 的状态( 相邻),记作 :
- 对于一条从 走到 的路径,若其顺时针走包含 ,则在 中添加 的路径,否则其便然逆时针走包含 ,不添加该条路径。
此时 为一个路径集合。下面说明对于两个路径集合 ,若 ,则 所对应的环上路径必然不能都不维护:
因为 ,所以 中必然存在一个路径 满足其在 中恰好出现了一次。考虑到若 对应路径同时不维护,则对于上面的 路径而言,其无法顺时针或逆时针来从 移动到 而不经过任何不维护的路段。因此可以证明选择的删除集合 必然不能同时包含 。
然后下面说明若 ,则该删除方案必然可行:此时只需要按照 中维护出现的路径集合 ,按照 的方式来顺时针 / 逆时针维护路径即可。
因此可以证明,答案必然是选择一个合法的路径集合 ,然后对于所有 的环上路径,停止对其的维护,其余路径全部维护。此时问题变为如何快速的记录 的值。考虑对于每一条路径 随机一个整数 ,然后让 顺时针下所有经过的边全部异或上 ,这个操作可以使用差分来实现。最后只需要用 map 求出所有 的众数,用 减去她即可。时间复杂度为 。
#pragma GCC optimize(3, "Ofast", "inline")
#include <bits/stdc++.h>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#define int long long
using namespace std;
const int N = 500010;
int diff[N];
mt19937_64 mt(time(0));
signed main() {
cin.tie(0)->sync_with_stdio(false);
cout << fixed << setprecision(15);
int T;
cin >> T;
while (T--) {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; ++i) diff[i] = 0;
for (int i = 1; i <= m; ++i) {
int a, b;
cin >> a >> b;
int rng = mt();
diff[a] ^= rng, diff[b] ^= rng;
}
for (int i = 2; i <= n; ++i) diff[i] ^= diff[i - 1];
map<int, int> mp;
for (int i = 1; i <= n; ++i) ++mp[diff[i]];
int res = n;
for (auto &[i, j] : mp) res = min(res, n - j);
cout << res << '\n';
}
return 0;
}
喜欢的话,留下你的评论吧~