CF1648F Two Avenues
首先发现问题是在一般图上的,非常难以处理,考虑从简单情况入手。
如果给定的是一棵树,我们只需要找到两条经过次数最多的边就好了。那是不是缩个点双就做完了呢?发现如果选的不是割边是很棘手的问题。
那么考虑简单环怎么做。假设当前第
而如果我们不割第
我们设
接下来我们考虑将第
发现由第一个式子可知这个式子肯定是大于零的,也就是说对于第
那么我们就可以分治配合主席树维护出
计算断两条边的贡献需要用到主席树。
考虑一般情况。首先缩边双仍然是不好做的,考虑性质也很优美的搜索树。由于这道题的限制很强,我们发现真正有意义的割的方法只有三种,分别为:
- 割两条割边。
- 割一条树边,一条返祖边,满足这条返祖边是唯一经过树边的。这里的经过它的返祖边定义为从它的子孙出发到它的祖先的边。
- 割两条树边,满足经过他们的返祖边边集相同。
容易证明只存在这三种情况答案不为
第一种情况是好做的,算经过次数最多的两条割边即可。
第二种我们枚举树边,利用异或哈希判断是否只有一条边经过它即可。
第三种情况我们考虑把集合相同的分别处理,处理与环的处理类似。
然后我们就清晰地讨论完了这三种情况。只要思路清晰,代码是不难写的,我也就写了两百行调了几个小时而已。
代码:
#include <bits/stdc++.h>
#define rep(i, l, r) for (int i (l); i <= (r); ++ i)
#define rrp(i, l, r) for (int i (r); i >= (l); -- i)
#define eb emplace_back
#define pii pair <int, int>
#define inf 1000000000
using namespace std;
constexpr int N = 2e5 + 5, M = 105, P = 1e9 + 7;
typedef long long ll;
typedef unsigned long long ull;
inline int rd () {
int x = 0, f = 1;
char ch = getchar ();
while (! isdigit (ch)) {
if (ch == '-') f = -1;
ch = getchar ();
}
while (isdigit (ch)) {
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar ();
}
return x * f;
}
int qpow (int x, int y, int p = P) {
int ret = 1;
for (; y; y >>= 1, x = x * x % p) if (y & 1) ret = ret * x % p;
return ret;
}
int n, m;
vector <int> e[N], g[N], vec[N];
int dfn[N], dfu[N], tim;
int sum[N << 7], ls[N << 7], rs[N << 7], tot;
int rt[N];
int pa[N][22];
mt19937_64 rnd (20090427);
ull c[N];
map <ull, pii> mp;
map <ull, vector <int> > G;
int dep[N];
int k, ga[N], gb[N];
void dfs (int u, int fa) {
dfn[u] = ++ tim; pa[u][0] = fa;
dep[u] = dep[fa] + 1;
for (auto v : e[u]) {
if (! dfn[v]) dfs (v, u), g[u].eb (v);
else if (dep[v] < dep[u] - 1) {
ull w (rnd ());
mp[w] = pii (u, v);
c[u] ^= w, c[v] ^= w;
}
} dfu[u] = tim;
}
int LCA (int x, int y) {
if (dep[x] < dep[y]) swap (x, y);
rrp (i, 0, 21) if (dep[pa[x][i]] >= dep[y]) x = pa[x][i];
if (x == y) return x;
rrp (i, 0, 21) if (pa[x][i] ^ pa[y][i]) x = pa[x][i], y = pa[y][i];
return pa[x][0];
}
void upd (int &p, int pre, int l, int r, int x, int k) {
p = ++ tot;
sum[p] = sum[pre];
ls[p] = ls[pre];
rs[p] = rs[pre];
sum[p] += k;
if (l == r) return ;
int mid (l + r >> 1);
if (x <= mid) upd (ls[p], ls[pre], l, mid, x, k);
else upd (rs[p], rs[pre], mid + 1, r, x, k);
}
int qry (int pre, int p, int l, int r, int L, int R) {
// cout<<sum[pre]<<" "<<sum[ls[pre]]+sum[rs[pre]]<<" "<<pre<<endl;
if (L <= l && r <= R) return sum[p] - sum[pre];
int mid (l + r >> 1), ret (0);
if (L <= mid) ret += qry (ls[pre], ls[p], l, mid, L, R);
if (R > mid) ret += qry (rs[pre], rs[p], mid + 1, r, L, R);
return ret;
}
int mx, smx;
int mv, smv;
int ans;
int edge[2][2];
vector <int> a;
void dfs (int u) {
for (auto v : g[u]) dfs (v), c[u] ^= c[v];
if (u > 1) {
int val (sum[rt[dfu[u]]] - sum[rt[dfn[u] - 1]]);
if (! c[u]) {
if (mv < val) {
smv = mv, smx = mx;
mv = val, mx = u;
} else if (smv < val) {
smv = val, smx = u;
}
} else {
if (mp.find (c[u]) != mp.end ()) {
if (val > ans) {
ans = val;
edge[0][0] = u, edge[0][1] = pa[u][0];
edge[1][0] = mp[c[u]].first;
edge[1][1] = mp[c[u]].second;
}
}
G[c[u]].eb (u);
}
}
}
int calc (int u, int v) {
int ret = sum[rt[dfu[v]]] - sum[rt[dfn[v] - 1]] + sum[rt[dfu[u]]] - sum[rt[dfn[u] - 1]];
int s = 0;
if (dfn[v] > 1) s += qry (rt[dfn[u] - 1], rt[dfu[u]], 1, n, 1, dfn[v] - 1);
if (dfu[v] < n) s += qry (rt[dfn[u] - 1], rt[dfu[u]], 1, n, dfu[v] + 1, n);
ret -= s * 2;
return ret;
}
void solve (int l, int r, int L, int R) {
if (l > r) return ;
int mid (l + r >> 1), ret (0), p (0);
rep (i, L, min (R, mid)) {
int val (calc (a[i], a[mid]));
if (ret < val) ret = val, p = i;
}
if (ans < ret) {
ans = ret;
edge[0][0] = a[mid], edge[0][1] = pa[a[mid]][0];
edge[1][0] = a[p], edge[1][1] = pa[a[p]][0];
}
solve (l, mid - 1, L, p), solve (mid + 1, r, p, R);
}
int id;
void solve () {
n = rd (), m = rd (); ++ id;
rep (i, 1, m) {
int u (rd ()), v (rd ());
e[u].eb (v), e[v].eb (u);
} dfs (1, 0);
rep (j, 1, 21) rep (i, 1, n) pa[i][j] = pa[pa[i][j - 1]][j - 1];
k = rd ();
rep (i, 1, k) ga[i] = rd (), gb[i] = rd ();
rep (i, 1, k) {
int u (ga[i]), v (gb[i]);
vec[dfn[u]].eb (dfn[v]);
vec[dfn[v]].eb (dfn[u]);
vec[dfn[LCA (u, v)]].eb (- dfn[u]);
vec[dfn[LCA (u, v)]].eb (- dfn[v]);
}
rep (i, 1, n) {
rt[i] = rt[i - 1];
for (auto j : vec[i]) {
if (j > 0) upd (rt[i], rt[i], 1, n, j, 1);
else upd (rt[i], rt[i], 1, n, -j, -1);
}
}
dfs (1);
if (mv + smv > ans) {
ans = mv + smv;
edge[0][0] = mx, edge[0][1] = pa[mx][0];
edge[1][0] = smx, edge[1][1] = pa[smx][0];
}
for (auto p : G) if (p.second.size () >= 2) {
a = p.second, solve (0, a.size () - 1, 0, a.size () - 1);
}
if (! ans) {
edge[0][0] = edge[1][0] = 0;
rep (u, 1, n) for (auto v : e[u]) {
if (u > v) continue;
if (! edge[0][0]) edge[0][0] = u, edge[0][1] = v;
else if (! edge[1][0]) edge[1][0] = u, edge[1][1] = v;
}
}
cout << ans << endl;
cout << edge[0][0] << " " << edge[0][1] << "\n";
cout << edge[1][0] << " " << edge[1][1] << "\n";
G.clear (), mp.clear ();
rep (i, 1, n) g[i].clear (), e[i].clear (), vec[i].clear (), c[i] = 0, dfn[i] = dfu[i] = 0, rt[i] = 0;
rep (i, 1, tot) sum[i] = ls[i] = rs[i] = 0;
tot = tim = 0; ans = -1, mx = smx = 0, mv = smv = 0;
}
int32_t main () {
// freopen ("1.out", "w", stdout);
rd ();
for (int T (rd ()); T; -- T) solve ();
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?