ARC181E Min and Max at the edge

先考虑怎么判断一个图是好图。感性上我们选取的区间一定是比较小的,因此考虑以 \(|u-v|\) 为边权求一棵最小生成树。不难说明,如果图是好图,那么这样找出来的就是它的唯一一棵合法生成树。

我们先求出原图的生成树。如果割掉的边是非树边,那么直接判就好了。考虑割掉树边的情况。

不妨设断掉了边 \((u,v)\),那么整个图被分成了两个连通块,连通块之间多了若干条非树边,形如 \([l_1,r_1],[l_2,r_2],\cdots,[l_k,r_k]\),这里直接写成了区间的形式。

假设我们新换上了区间 \([l_1,r_1]\),那么需要满足 \(l_1=\max l\)\(r_1=\min r\)。如果不存在这样的区间那么割掉 \((u,v)\) 就不合法。

然后我们考虑剩下的非树边会不会满足条件。称一条非树边 \((x,y)\) 是合法的当且仅当树上 \(x\to y\) 的路径上每个点都在 \(x\)\(y\) 之间。

如果我们换上了一条合法的边,在判掉上面的部分后,我们可以说明其它非树边的合法性是不会改变的。

设非树边为 \((x,y)\),我们割掉了 \((u,v)\),换上了 \((u',v')\)。那么一侧的路径就从 \(x\to u\) 变成了 \(x\to u'\)。因为 \((x,y)\) 代表的区间是包含了 \((u',v')\),并且原树上 \((u',v')\) 合法,所以路径 \(x\to u\to u'\) 是和 \(x\to u'\) 等价的。而 \(u\to u'\)\(v\to v'\) 两条路径也和 \((u',v')\) 边等效,上面已经判了 \((u',v')\) 包含于 \((x,y)\),所以 \(x\to u\to u'\) 也等价于 \(x\to u\),即 \(x\to u\)\(x\to u'\) 是等价的。(是不是说的有点绕口令……)

总之只有在原树所有非树边都是合法时,换上一条合法边才有可能合法。

现在我们考虑换上了不合法边的情况。如果新图是好图,那么所有的不合法边在新图上需要变成合法。这说明我们断掉边 \((u,v)\) 后所有原来的不合法边都要是连接两个连通块的。那么此时我们换上来的不合法边就是其中边权最小的边。这说明我们换上的不合法边其实是唯一的!

设那一条可能换上的不合法边 \((x,y)\),取出原树上 \(x\to y\) 的链 \(x=S_1\to S_2\to\cdots\to S_k=y\),每个点属于其中某个 \(S_i\) 的子树。割掉的边 \((u,v)\) 也一定是链上的一条边。

对于一条原来不合法的边 \((x',y')\),设 \(x'\)\(y'\) 所属的子树分别为 \(i,j\)\(i\le j\)。割掉的边 \((u,v)\) 需要在原树 \(x'\to y'\) 的路径上,即是 \(i\)\(j\) 区间内的一条边。

对于原来一条合法的边,如果它们在某一棵子树内就不用管了。否则如果断掉了 \(i\)\(j\) 之间的一条边,那么新的路径就是 \(x'\to S_i\to x\)\(y'\to S_j\to y\),这个可以预处理之后判一下。如果不合法了就说明不能断 \(i\)\(j\) 之间的边,否则就是没有限制的。

所以我们要进行的就是对区间里的边禁用,差分一下即可。那么总复杂度就是 \(O(n\log n)\)

感觉细节有点多,代码写得非常混乱。

顺便提一下标算的做法:我们不一定要按照 \(|u-v|\) 排序,只要保证被包含的区间要在前面就好。因此一个考虑是按 \((-u,v)\) 做最小生成树。同理我们还可以用 \((v,-u)\) 做。事实上两棵生成树相同就是对的了,但是好像不太会证。之后就是简单的了。

#include <bits/stdc++.h>
#include <ext/pb_ds/priority_queue.hpp>
#define eb emplace_back
using namespace std;
using pii = pair<int, int> ;
template <typename T> void Chkmin(T &x, T y) { x = min(x, y); }
template <typename T> void Chkmax(T &x, T y) { x = max(x, y); }

const int kN = 2e5 + 5;
int n, m;
int u[kN], v[kN], ord[kN];
bool mst[kN], ans[kN], flag[kN];
vector<pii> g[kN];

struct DSU {
  int fa[kN];
  DSU() { iota(fa, fa + kN, 0); }
  int Find(int x) {
    return (fa[x] == x) ? x : (fa[x] = Find(fa[x]));
  }
  bool Merge(int x, int y) {
    x = Find(x);
    y = Find(y);
    if(x == y) return 0;
    return fa[x] = y, 1;
  }
}dsu;

int fa[kN], jp[kN], dep[kN], mn[kN], mx[kN], fid[kN];

void DFS(int x, int fa) {
  ::fa[x] = fa;
  dep[x] = dep[fa] + 1;
  int f1 = jp[fa], f2 = jp[f1];
  if(dep[f1] * 2 == dep[fa] + dep[f2]) {
    jp[x] = f2;
    mn[x] = min({x, mn[fa], mn[f1]});
    mx[x] = max({x, mx[fa], mx[f1]});
  }else {
    jp[x] = fa;
    mn[x] = mx[x] = x;
  }
  for(pii k : g[x]) {
    int to, id;
    tie(to, id) = k;
    if(to != fa) fid[to] = id, DFS(to, x);
  }
}
bool In(int x, int y) {
  while(dep[y] > dep[x]) y = (dep[jp[y]] >= dep[x]) ? jp[y] : fa[y];
  return x == y;
}
tuple<int, int, int> LCA(int x, int y) {
  int mn = n + 1, mx = 0;
  if(dep[x] < dep[y]) swap(x, y);
  while(dep[x] > dep[y]) {
    if(dep[jp[x]] >= dep[y]) {
      Chkmin(mn, ::mn[x]);
      Chkmax(mx, ::mx[x]);
      x = jp[x];
    }else {
      Chkmin(mn, x);
      Chkmax(mx, x);
      x = fa[x];
    }
  }
  while(x != y) {
    if(jp[x] != jp[y]) {
      Chkmin(mn, min(::mn[x], ::mn[y]));
      Chkmax(mx, max(::mx[x], ::mx[y]));
      x = jp[x], y = jp[y];
    }else {
      Chkmin(mn, min(x, y));
      Chkmax(mx, max(x, y));
      x = fa[x], y = fa[y];
    }
  }
  Chkmin(mn, x);
  Chkmax(mx, x);
  return make_tuple(mn, mx, x);
}
void Path(int u, int v, vector<int> &np, vector<int> &ep) {
  vector<int> fu, fv;
  vector<int> eu, ev;
  while(u != v) {
    if(dep[u] >= dep[v]) {
      fu.eb(u);
      eu.eb(fid[u]);
      u = fa[u];
    }else {
      fv.eb(v);
      ev.eb(fid[v]);
      v = fa[v];
    }
  }
  fu.eb(u);
  reverse(fv.begin(), fv.end());
  reverse(ev.begin(), ev.end());
  fu.insert(fu.end(), fv.begin(), fv.end());
  eu.insert(eu.end(), ev.begin(), ev.end());
  np = fu, ep = eu;
}

struct PQ {
  __gnu_pbds::priority_queue<int> q1, q2;
  void Push(int x) { q1.push(x); }
  void Erase(int x) { q2.push(x); }
  bool Empty() { return q1.size() == q2.size(); }
  int Top() {
    while(q2.size() && (q1.top() == q2.top())) q1.pop(), q2.pop();
    return q1.top();
  }
  void Join(PQ &rhs) {
    q1.join(rhs.q1);
    q2.join(rhs.q2);
  }
};
int chg[kN];
map<pii, int> mp;
PQ ql[kN], qr[kN];
vector<pii> updl[kN], updr[kN];

void DFS2(int x, int fa, int eid) {
  for(pii k : g[x]) {
    int to, id;
    tie(to, id) = k;
    if(to == fa) continue;
    DFS2(to, x, id);
    ql[x].Join(ql[to]);
    qr[x].Join(qr[to]);
  }
  for(pii k : updl[x]) {
    int v, f;
    tie(v, f) = k;
    if(f) ql[x].Push(v);
    else ql[x].Erase(v), ql[x].Erase(v);
  }
  for(pii k : updr[x]) {
    int v, f;
    tie(v, f) = k;
    if(f) qr[x].Push(v);
    else qr[x].Erase(v), qr[x].Erase(v);
  }
  if(eid && !ql[x].Empty()) {
    int l = ql[x].Top(), r = -qr[x].Top();
    auto it = mp.find(pii {l, r});
    if(it != mp.end()) {
      int id = it -> second;
      if(!mst[id]) {
        bool flag = 0;
        bool fu = In(x, u[id]), fv = In(x, v[id]);
        chg[eid] = (fu ^ fv) * id;
      }
    }
  }
}

int bel[kN];
int d[kN];
bool inp[kN];

void DFS3(int x, int fa, int b) {
  bel[x] = b;
  for(pii k : g[x]) {
    int to = k.first;
    if((to != fa) && !inp[to]) DFS3(to, x, b);
  }
}

int mnu[kN], mxu[kN], mnv[kN], mxv[kN];
void DFS4(int x, int fa, int *mn, int *mx) {
  if(!fa) mn[x] = mx[x] = x;
  else {
    Chkmin(mn[x], x);
    Chkmax(mx[x], x);
  }
  for(pii k : g[x]) {
    int to = k.first;
    if(to != fa) {
      mn[to] = mn[x];
      mx[to] = mx[x];
      DFS4(to, x, mn, mx);
    }
  }
}

int main() {
//  freopen("1.in", "r", stdin);
//  freopen("1.out", "w", stdout);
  ios::sync_with_stdio(0), cin.tie(0);
  cin >> n >> m;
  for(int i = 1; i <= m; i++) {
    cin >> u[i] >> v[i];
    if(u[i] > v[i]) swap(u[i], v[i]);
    mp[pii {u[i], v[i]}] = i;
  }
  iota(ord + 1, ord + m + 1, 1);
  sort(ord + 1, ord + m + 1,
    [&](int x, int y) { return v[x] - u[x] < v[y] - u[y]; });
  for(int i = 1; i <= m; i++) {
    int e = ord[i];
    if(mst[e] = dsu.Merge(u[e], v[e])) {
      g[u[e]].eb(v[e], e);
      g[v[e]].eb(u[e], e);
    }
  }
  DFS(1, 0);
  int cnt0 = 0, id = 0;
  for(int i = m; i >= 1; i--) {
    int e = ord[i];
    if(mst[e]) continue;
    int mn, mx, lca;
    tie(mn, mx, lca) = LCA(u[e], v[e]);
    int l = u[e], r = v[e];
    flag[e] = ((mn == l) && (mx == r));
    if(!flag[e]) cnt0++, id = e;
    updl[l].eb(l, 1);
    updl[r].eb(l, 1);
    updl[lca].eb(l, 0);
    updr[l].eb(-r, 1);
    updr[r].eb(-r, 1);
    updr[lca].eb(-r, 0);
  }
  if(cnt0 <= 1) {
    for(int i = 1; i <= m; i++) {
      if(!mst[i]) ans[i] = (!cnt0 || (i == id));
    }
  }
  DFS2(1, 0, 0);
  if(!cnt0) {
    for(int i = 1; i <= m; i++) {
      if(mst[i]) ans[i] = (chg[i] != 0);
    }
    for(int i = 1; i <= m; i++) {
      cout << (ans[i] ? "Yes\n" : "No\n");
    }
    return 0;
  }
  vector<int> np, ep;
  Path(u[id], v[id], np, ep);
  for(int i : np) inp[i] = 1;
  for(int i = 0; i < np.size(); i++) DFS3(np[i], 0, i);
  DFS4(u[id], 0, mnu, mxu);
  DFS4(v[id], 0, mnv, mxv);
  for(int i = 1; i <= m; i++) {
    if(mst[i] || (i == id)) continue;
    int bu = bel[u[i]];
    int bv = bel[v[i]];
    if(bu == bv) {
      if(flag[i]) continue;
      d[0]++;
      break;
    }
    int l = u[i], r = v[i];
    if(bu > bv) swap(u[i], v[i]), swap(bu, bv);
    int mn = min(mnu[u[i]], mnv[v[i]]);
    int mx = max(mxu[u[i]], mxv[v[i]]);
    if(!flag[i]) d[0]++, d[bu]--, d[bv]++;
    if((mn != l) || (mx != r)) {
      if(!flag[i]) { d[0]++; break; }
      else d[bu]++, d[bv]--;
    }
  }
  for(int i = 1; i < np.size(); i++) d[i] += d[i - 1];
  for(int i = 0; i + 1 < np.size(); i++) {
    ans[ep[i]] = ((chg[ep[i]] == id) && !d[i]);
  }
  for(int i = 1; i <= m; i++) {
    cout << (ans[i] ? "Yes\n" : "No\n");
  }
  return 0;
}
posted @ 2025-09-07 19:21  CJzdc  阅读(14)  评论(0)    收藏  举报