BZOJ 3514

给一个n个点m条边的无向图,k次询问只保留编号在[l,r]中的边时图中的联通块个数,强制在线。

$$n,m,k \leq 2\times 10^5$$

依次插入边,用LCT维护生成森林。插入第$i$条边时,如果形成环,就把环上编号最小的边删掉,并记$pre_i$为删掉的边的编号。每次查询时,$[l,r]$中$pre_i<l$的边可以将联通块个数-1,反之不行,用主席树维护即可。

const int MAXN = 200000 + 5, LOG = 25;

struct Input {
  char buf[1 << 25], *s;
  
  Input() {
#ifdef LOCAL
    freopen("BZOJ3514.in", "r", stdin);
    freopen("BZOJ3514.out", "w", stdout);
#endif
    fread(s = buf, 1, 1 << 25, stdin);
  }
  
  friend Input &operator>>(Input &io, int &x) {
    x = 0;
    while (!isdigit(*io.s))
      ++ io.s;
    while (isdigit(*io.s))
      x = x * 10 + *io.s ++ - '0';
    return io;
  }
} cin;

struct LinkCutTree {
  struct Node {
    Node *fa, *ch[2];
    int pre, next, first, last, min;
    bool flip;
    
    Node() {
      fa = ch[0] = ch[1] = NULL;
      pre = next = first = last = min = INF;
      flip = 0;
    }
    
    bool relation() {
      return this == fa->ch[1];
    }
    
    bool is_root() {
      return !fa || (fa->ch[0] != this && fa->ch[1] != this);
    }
    
    void push_up() {
      min = INF;
      if (ch[0]) {
        chkmin(min, pre);
        chkmin(min, ch[0]->min);
        first = ch[0]->first;
      } else {
        first = pre;
      }
      if (ch[1]) {
        chkmin(min, next);
        chkmin(min, ch[1]->min);
        last = ch[1]->last;
      } else {
        last = next;
      }
    }
    
    void push_down() {
      if (flip) {
        FOR(i, 0, 2) {
          if (ch[i]) {
            std::swap(ch[i]->pre, ch[i]->next);
            std::swap(ch[i]->first, ch[i]->last);
            ch[i]->flip ^= 1;
          }
        }
        std::swap(ch[0], ch[1]);
        flip = 0;
      }
    }
  } *node[MAXN];
  
  void make_tree(int n) {
    For(i, 1, n) {
      node[i] = new Node();
    }
  }
  
  void rotate(Node *o) {
    Node *par = o->fa;
    int dir = o->relation();
    par->ch[dir] = o->ch[dir ^ 1];
    if (o->ch[dir ^ 1]) {
      o->ch[dir ^ 1]->fa = par;
    }
    if (!par->is_root()) {
      par->fa->ch[par->relation()] = o;
    }
    o->fa = par->fa;
    par->fa = o;
    o->ch[dir ^ 1] = par;
    par->push_up();
    o->push_up();
  }
  
  void splay(Node *o) {
    static Node *stack[MAXN];
    int top = 0;
    Node *temp = o;
    for (stack[++ top] = temp; !temp->is_root(); temp = temp->fa) {
      stack[++ top] = temp->fa;
    }
    Rep(i, top, 1) {
      stack[i]->push_down();
    }
    for (; !o->is_root(); rotate(o)) {
      if (!o->fa->is_root()) {
        rotate(o->fa->relation() == o->relation() ? o->fa : o);
      }
    }
  }
  
#define _first(x) ((x) ? x->first : INF)
  void access(Node *o) {
    for (Node *temp = NULL; o; temp = o, o = o->fa) {
      splay(o);
      o->ch[1] = temp;
      if (temp)
        temp->fa = o;
      o->next = _first(temp);
      o->push_up();
    }
  }
  
  void make_root(Node *o) {
    access(o);
    splay(o);
    o->flip = 1;
    std::swap(o->pre, o->next);
    std::swap(o->first, o->last);
  }
  
  Node *find_root(Node *o) {
    access(o);
    splay(o);
    for (o->push_down(); o->ch[0]; o->push_down()) {
      o = o->ch[0];
    }
    splay(o);
    return o;
  }
  
  int query(Node *o, Node *p) {
    make_root(o);
    access(p);
    splay(p);
    return p->min;
  }
  
  void link(Node *o, Node *p, int val) {
    make_root(p);
    p->fa = o;
    p->pre = val;
    p->push_up();
  }
  
  void cut(Node *o, Node *p) {
    make_root(o);
    access(p);
    splay(p);
    p->ch[0] = o->fa = NULL;
    o->next = p->pre = INF;
    p->push_up();
    o->push_up();
  }
} T;

struct PresidentTree {
  int ch[2][MAXN * LOG], sum[MAXN * LOG], root[MAXN], cnt;
  
  void insert(int &o, int p, int l, int r, int x) {
    o = ++ cnt;
    ch[0][o] = ch[0][p], ch[1][o] = ch[1][p], sum[o] = sum[p] + 1;
    if (l == r) {
      return;
    }
    int mid = (l + r) >> 1;
    if (x <= mid) {
      insert(ch[0][o], ch[0][p], l, mid, x);
    } else {
      insert(ch[1][o], ch[1][p], mid + 1, r, x);
    } 
  }
  
  int query(int o, int p, int l, int r, int x) {
    if (l == r)
      return sum[o] - sum[p];
    int mid = (l + r) >> 1;
    if (x <= mid)
      return query(ch[0][o], ch[0][p], l, mid, x);
    else
      return query(ch[1][o], ch[1][p], mid + 1, r, x) + sum[ch[0][o]] - sum[ch[0][p]];
  }
} Seg;

struct Edge {
  int from, to;
} edge[MAXN];

int main() {
  int n, m, k, type;
  cin >> n >> m >> k >> type;
  T.make_tree(n);
  For(i, 1, m) {
    cin >> edge[i].from >> edge[i].to;
    if (edge[i].from == edge[i].to) {
      Seg.insert(Seg.root[i], Seg.root[i - 1], 0, m, i);
      continue;
    }
    if (T.find_root(T.node[edge[i].from]) == T.find_root(T.node[edge[i].to])) {
      int num = T.query(T.node[edge[i].from], T.node[edge[i].to]);
      T.cut(T.node[edge[num].from], T.node[edge[num].to]);
      T.link(T.node[edge[i].from], T.node[edge[i].to], i);
      Seg.insert(Seg.root[i], Seg.root[i - 1], 0, m, num);
    } else {
      T.link(T.node[edge[i].from], T.node[edge[i].to], i);
      Seg.insert(Seg.root[i], Seg.root[i - 1], 0, m, 0);
    }
  }
  int last_ans = 0;
  For(i, 1, k) {
    int l, r;
    cin >> l >> r;
    if (type) {
      l ^= last_ans;
      r ^= last_ans;
    }
    printf("%d\n", last_ans = n - Seg.query(Seg.root[r], Seg.root[l - 1], 0, m, l - 1));
  }
  return 0;
}

 

转载于:https://www.cnblogs.com/sjkmost/p/9825309.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值