Loading

CF1648F Two Avenues

首先发现问题是在一般图上的,非常难以处理,考虑从简单情况入手。

如果给定的是一棵树,我们只需要找到两条经过次数最多的边就好了。那是不是缩个点双就做完了呢?发现如果选的不是割边是很棘手的问题。

那么考虑简单环怎么做。假设当前第 i 条边为 (i,i+1),第 n 条边为 (n,1)。如果我们割第 n 条边,那么我们只需要找到一个被经过次数最多的边选上就好了。

而如果我们不割第 n 条边,直接做是不好做的。那么我们考虑枚举选的一条边,并对每条边找到一条答案最大的边,我们不妨设枚举的那条边编号更大,发现也不好想,那么我们可以思考相邻两条边之间的配对边的关系。

我们设 fi 表示小于 i 且跟第 i 条边配对答案最大的边,我们发现它是存在单调性的,证明考虑反证,如果存在 fi+1<fi,我们考虑证明这种情况下对于第 i 条边来说 fi+1 更优。对于第 i+1 条边,若将 fi+1 变为 fi 那么变化的量要小于零,即为

j=1k[fi+1<ajfibji+1]j=1k[fi+1<ajfibj>i+1]<0

接下来我们考虑将第 i 条边的配对边改为 fi+1 的变化量,其变化量为

j=1k[fi+1<ajfibj>i]j=1k[fi+1<ajfibji]

发现由第一个式子可知这个式子肯定是大于零的,也就是说对于第 i 条边 fi+1 更优,与 fi 最优矛盾,证毕。

那么我们就可以分治配合主席树维护出 f,时间复杂度 O(nlog2n)

计算断两条边的贡献需要用到主席树。

考虑一般情况。首先缩边双仍然是不好做的,考虑性质也很优美的搜索树。由于这道题的限制很强,我们发现真正有意义的割的方法只有三种,分别为:

  1. 割两条割边。
  2. 割一条树边,一条返祖边,满足这条返祖边是唯一经过树边的。这里的经过它的返祖边定义为从它的子孙出发到它的祖先的边。
  3. 割两条树边,满足经过他们的返祖边边集相同。

容易证明只存在这三种情况答案不为 0。维护返祖边边集可以用异或哈希简单维护,现在考虑处理这三种情况。

第一种情况是好做的,算经过次数最多的两条割边即可。

第二种我们枚举树边,利用异或哈希判断是否只有一条边经过它即可。

第三种情况我们考虑把集合相同的分别处理,处理与环的处理类似。

然后我们就清晰地讨论完了这三种情况。只要思路清晰,代码是不难写的,我也就写了两百行调了几个小时而已。

代码:

#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 ();
}

作者:lalaouye

出处:https://www.cnblogs.com/lalaouyehome/p/18739536

版权:本作品采用「114514」许可协议进行许可。

posted @   lalaouye  阅读(1)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示