P8531 [Ynoi2003] 戌亥彗星

特殊性质实际上就是保证了所有环外点度数都 \(\le2\),这样就只需要考虑前两个条件。注意到对于一个 \(i\),假设 \(i\) 为区间左端点,那么所有满足条件 \(2\) 的右端点构成一个区间,记为 \(l_i,r_i\),且满足 \(l_i\le l_{i+1},r_i\le r_{i+1}\)

而且这些区间有更强的性质:如果 \(l_i<l_{i+1}\),则 \(r_i<l_{i+1}\)。证明也是不难的:如果 \(r_i\ge l_{i+1}\),那么 \([i,l_i]\)\([i+1,l_{i+1}]\) 中的简单环在 \([i,r_i]\) 中也会出现。因为 \(l_i<l_{i+1}\),存在一个简单环包含了 \(i\),于是 \([i,r_i]\) 中有 \(>1\) 个简单环,与定义矛盾。

现在考虑加入条件 \(3\),对于每个 \(i\),同时满足条件 \(2,3\) 的右端点也形成一个区间,且区间左端点为 \(l_i\)。记这个区间为 \([l_i,r'_i]\),如果没有满足条件的右端点,记 \(r'_i=l_i-1\)。显然有 \(l_i-1\le r'_i\le r_i\)

事实上 \(r'\) 也满足 \(r'_i\le r'_{i+1}\)。证明考虑如果 \(l_i=l_{i+1}\),根据定义就有单调性;如果 \(l_i<l_{i+1}\),那么 \(r'_i\le r_i\le l_{i+1}-1\le r'_{i+1}\)

考虑怎么求 \(l\)\(r'\)。由于两个序列都有单调性,可以类似双指针地做。具体来说,动态维护 \([l_i,r'_i]\) 的边组成的结构,每次从 \(i\) 推向 \(i-1\) 时,暴力移动右端点直到满足条件 \(2,3\)。如果求出的 \(r'_{i-1}<l_i\),那么再暴力移动右端点找出 \(l_{i-1}\),然后复原即可;否则有 \(l_{i-1}=l_i\)。因为如果 \(l_{i-1}<l_i\),那么 \(r'_{i-1}\le r_{i-1}<l_i\),所以暴力找 \(l\) 是对的。移动端点的总次数为 \(O(n)\)

这个结构需要支持的操作为断边、加边和查询是否满足条件 \(2,3\),考虑 LCT。因为任意时刻都只有 \(\le1\) 个基环树,可以在森林的基础上用一个变量记录环边。条件 \(3\) 只需要支持查环上度数 \(\ge3\) 的点个数,再与整体比对,因为我们有了一条环边,可以直接查询两端点路径。条件 \(2\) 也是好判定的,加边时分讨一下两端是否连通,连通再讨论是否已经有环边即可。

现在我们已经求出了 \(l_i\)\(r'_i\)。考虑把询问记录在左端点,然后扫描线。如果已经满足了条件 \(2,3\),条件 \(1\) 的限制就是恰有一个连通块。显然该连通块为一棵基环树,有 \(|V|=|E|\)。考虑在对 \(l\) 扫描时,对每个 \(r\),维护对应的 \(d_r=|V|-|E|\),同时记录合法的 \(l\) 个数 \(c_r\)。每次更新 \(d\) 后,找出所有在区间 \([l_i,r'_i]\)\(d_p=0\) 的位置 \(p\),把对应的 \(c_p\leftarrow c_p+1\) 即可。因为我们已经保证了条件 \(2,3\),所以有 \(|V|-|E|\le0\)。每次就是相当于找出最大的 \(d_p\) 如果 \(=0\) 则将 \(c_p\leftarrow c_p+1\)。这个是容易用线段树维护的。

总时间复杂度为 \(O((n+q)\log n)\)

#include <bits/stdc++.h>
#define ALL(x) begin(x), end(x)
#define All(x, l, r) &x[l], &x[r] + 1
using namespace std;
void file() {
  freopen("1.in", "r", stdin);
  freopen("1.out", "w", stdout);
}
using ll = long long;
template <typename T> using vec = vector<T> ;

const int kL = 1e6 + 5;
int n, q;
array<int, kL> l, r;

struct edge {
  int u, v;
  edge() { }
};
array<edge, kL> ed;

namespace Solve1 {
  int cir;
  array<int, kL> deg;

  namespace LCT {
    int cntall;
    array<bool, kL> rev;
    array<int, kL> fa, cnt;
    array<array<int, 2>, kL> ch;
    void pu(int x) { cnt[x] = cnt[ch[x][0]] + cnt[ch[x][1]] + (deg[x] >= 3); }
    bool wc(int x) { return ch[fa[x]][1] == x; }
    bool ir(int x) { return (ch[fa[x]][0] ^ x) && (ch[fa[x]][1] ^ x); }
    void rot(int x) {
      int y = fa[x], z = fa[y], l = wc(x), r = !l;
      if(!ir(y)) ch[z][wc(y)] = x;
      if(ch[y][l] = ch[x][r]) fa[ch[y][l]] = y;
      fa[fa[ch[x][r] = y] = x] = z;
      pu(y); pu(x);
    }
    void add(int x) { rev[x] ^= 1; swap(ch[x][0], ch[x][1]); }
    void pd(int x) { if(rev[x]) { add(ch[x][0]); add(ch[x][1]); rev[x] = 0; } }
    void PD(int x) { if(!ir(x)) PD(fa[x]); pd(x); }
    void spl(int x) {
      for(PD(x); !ir(x); rot(x))
        if(!ir(fa[x])) rot((wc(x) ^ wc(fa[x])) ? x : fa[x]);
    }
    int acs(int x) {
      int tx = x, t = 0;
      for(; x; x = fa[t = x]) { spl(x); ch[x][1] = t; pu(x); }
      return spl(tx), t;
    }
    void mkr(int x) { acs(x); add(x); }
    void split(int x, int y) { mkr(y); acs(x); }
    void link(int x, int y) { mkr(x); mkr(y); fa[x] = y; }
    void cut(int x, int y) { split(x, y); ch[x][0] = fa[y] = 0; pu(x); }
    int find(int x) {
      for(acs(x); pd(x), ch[x][0]; x = ch[x][0]) ;
      return spl(x), x;
    }
    int query(int x, int y) { return split(x, y), cnt[x]; }

    void update(int x, int v) {
      if(v > 0) {
        if((deg[x] += v) == 3) { cntall++; acs(x); pu(x); }
      }else {
        if((deg[x] += v) == 2) { cntall--; acs(x); pu(x); }
      }
    }
    bool insert(int id) {
      int u = ed[id].u, v = ed[id].v;
      if(find(u) == find(v)) {
        if(!cir) return cir = id, 1;
        else return 0;
      }
      return link(u, v), 1;
    }
    void erase(int id) {
      if(cir == id) return void(cir = 0);
      cut(ed[id].u, ed[id].v);
      if(cir && insert(cir)) cir = 0;
    }
    bool check() {
      if(!cir) return 0;
      int u = ed[cir].u, v = ed[cir].v;
      return query(u, v) == cntall;
    }
  }
  using LCT::check;
  
  bool insert(int id) {
    if(LCT::insert(id)) {
      LCT::update(ed[id].u, 1);
      LCT::update(ed[id].v, 1);
      return 1;
    }
    return 0;
  }
  void erase(int id) {
    LCT::update(ed[id].u, -1);
    LCT::update(ed[id].v, -1);
    LCT::erase(id);
  }

  void solve() {
    l[n] = n + 1; r[n] = n; insert(n);
    for(int i = n - 1, p = n; i; i--) {
      for(; p >= i; erase(p--))
        if(insert(i)) {
          r[i] = p;
          if(!cir) l[i] = p + 1;
          break;
        }
      if(l[i]) continue;
      for(; p >= i; erase(p--))
        if(check() || !cir) break;
      r[i] = p;
      if(!check()) { l[i] = p + 1; continue; }
      if((r[i] < l[i + 1]) || (l[i + 1] > r[i + 1])) {
        int tp = p;
        for(; p >= i; erase(p--))
          if(!check()) { l[i] = p + 1; break; }
        for(; p < tp; insert(++p)) ;
      }else l[i] = l[i + 1];
    }
  }
}

namespace Solve2 {
  const int sL = 4e6 + 5;
  array<ll, kL> ans;
  array<int, kL> last;
  array<vec<pair<int, int>>, kL> qry;

  #define ls (o << 1)
  #define rs (o << 1 | 1)

  struct SGT {
    array<int, sL> mx, t1, c;
    array<ll, sL> s, t2;
    void pu(int o) {
      s[o] = s[ls] + s[rs];
      mx[o] = max(mx[ls], mx[rs]);
      c[o] = (mx[ls] >= mx[rs]) * c[ls] + (mx[rs] >= mx[ls]) * c[rs];
    }
    void build(int o, int l, int r) {
      c[o] = r - l + 1;
      if(l == r) return ;
      int mi = (l + r) >> 1;
      build(ls, l, mi); build(rs, mi + 1, r);
    }
    void ad1(int o, int v) { mx[o] += v; t1[o] += v; }
    void ad2(int o, ll v) { s[o] += v * c[o]; t2[o] += v; } 
    void pd(int o) {
      if(int& x = t1[o]) { ad1(ls, x); ad1(rs, x); x = 0; }
      if(ll& x = t2[o]) {
        if(mx[ls] >= mx[rs]) ad2(ls, x);
        if(mx[ls] <= mx[rs]) ad2(rs, x);
        x = 0;
      }
    }
    void update(int o, int l, int r, int x, int y, int v) {
      if((l > y) || (r < x)) return ;
      if((l >= x) && (r <= y)) return ad1(o, v);
      pd(o); int mi = (l + r) >> 1;
      update(ls, l, mi, x, y, v); update(rs, mi + 1, r, x, y, v);
      pu(o);
    }
    void update2(int o, int l, int r, int x, int y, int v) {
      if((l > y) || (r < x) || (mx[o] < 0)) return ;
      if((l >= x) && (r <= y)) return assert(mx[o] == 0), ad2(o, v);
      pd(o); int mi = (l + r) >> 1;
      update2(ls, l, mi, x, y, v); update2(rs, mi + 1, r, x, y, v);
      pu(o);
    }
    ll query(int o, int l, int r, int x, int y) {
      if((l > y) || (r < x)) return 0;
      if((l >= x) && (r <= y)) return s[o];
      pd(o); int mi = (l + r) >> 1;
      return query(ls, l, mi, x, y) + query(rs, mi + 1, r, x, y);
    }
  }sgt;

  void solve() {
    for(int i = 1, l, r; i <= q; i++) {
      cin >> l >> r;
      qry[l].emplace_back(r, i);
    }
    last.fill(n + 1);
    sgt.build(1, 1, n);
    for(int i = n; i; i--) {
      sgt.update(1, 1, n, i, n, 1);
      sgt.update(1, 1, n, i, last[ed[i].u] - 1, -1);
      sgt.update(1, 1, n, i, last[ed[i].v] - 1, -1);
      sgt.update2(1, 1, n, l[i], r[i], 1);
      for(auto k : qry[i]) {
        int p = k.first, id = k.second;
        ans[id] = sgt.query(1, 1, n, i, p);
      }
      last[ed[i].u] = last[ed[i].v] = i;
    }
    for(int i = 1; i <= q; i++) cout << ans[i] << "\n";
  }
}

int main() {
  // file();
  ios::sync_with_stdio(0); cin.tie(0);
  cin >> n;
  for(int i = 1; i <= n; i++) cin >> ed[i].u >> ed[i].v;
  cin >> q;
  Solve1::solve();
  Solve2::solve();
  return 0;
}
posted @ 2024-10-07 20:02  CJzdc  阅读(8)  评论(0编辑  收藏  举报