BZOJ 3514: Codechef MARCH14 GERALD07加强版

3514: Codechef MARCH14 GERALD07加强版

Time Limit: 60 Sec  Memory Limit: 256 MB
Submit: 1356  Solved: 514
[Submit][Status][Discuss]

Description

N个点M条边的无向图,询问保留图中编号在[l,r]的边的时候图中的联通块个数。

Input

第一行四个整数N、M、K、type,代表点数、边数、询问数以及询问是否加密。
接下来M行,代表图中的每条边。
接下来K行,每行两个整数L、R代表一组询问。对于type=0的测试点,读入的L和R即为询问的L、R;对于type=1的测试点,每组询问的L、R应为L xor lastans和R xor lastans。

Output

K行每行一个整数代表该组询问的联通块个数。

Sample Input

3 5 4 0
1 3
1 2
2 1
3 2
2 2
2 3
1 5
5 5
1 2

Sample Output

2
1
3
1

HINT

 

对于100%的数据,1≤N、M、K≤200,000。


2016.2.26提高时限至60s

 

Source

[ Submit][ Status][ Discuss]

 

 

如果是离线的话,我们可以LCT+莫队什么的乱搞是吧,但是在线就……

不过还是有一个很喵的做法——

我们用LCT维护一棵生成树,当加入一条边$i$的时候($i$是其编号),其连接的两个点可能已经联通,加入$i$之后会形成一个环,我们弹掉这个环上编号最小的边(也就是加入最早的边),并记录其编号为$ntr_{i}$。特殊的,如果$i$没有弹掉任何边,我们记$ntr_{i}=0$。

对于一个询问$[L,R]$(表示我们只保留$e|e\in [L,R]$),答案就是$n-\sum_{i=L}^{R}{(ntr_{i}\lt L)}$。这个就是主席树了。

 

#include <cstdio>

inline int nextChar(void) {
  static const int siz = 1 << 20;
  static char buf[siz];
  static char *hd = buf + siz;
  static char *tl = buf + siz;
  if (hd == tl)
    fread(hd = buf, 1, siz, stdin);
  return int(*hd++);
}

inline int nextInt(void) {
  register int ret = 0;
  register int neg = false;
  register int bit = nextChar();
  for (; bit < 48; bit = nextChar())
    if (bit == '-')neg ^= true;
  for (; bit > 47; bit = nextChar())
    ret = ret * 10 + bit - '0';
  return neg ? -ret : ret;
}

template <class T>
inline void Swap(T &a, T &b) {
  T c;
  c = a;
  a = b;
  b = c;
}

template <class T>
inline T Max(const T &a, const T &b) {
  return a > b ? a : b;
}

template <class T>
inline T Min(const T &a, const T &b) {
  return a < b ? a : b;
}

const int mxn = 400005;
const int inf = 1000000007;

int n, m, q, e, ntr[mxn];

struct edge {
  int x, y;
}E[mxn];

namespace LCT {
  int top = 0;
  int stk[mxn];
  int val[mxn];
  int rev[mxn];
  int min[mxn];
  int fat[mxn];
  int son[mxn][2];

  inline bool isroot(int t) {
    int f = fat[t];
    if (!f)return true;
    if (son[f][0] == t)return false;
    if (son[f][1] == t)return false;
    return true;
  }

  inline void update(int t) {
    min[t] = val[t];
    if (son[t][0])min[t] = Min(min[t], min[son[t][0]]);
    if (son[t][1])min[t] = Min(min[t], min[son[t][1]]);
  }

  inline void push(int t) {
    rev[t] = 0;
    Swap(son[t][0], son[t][1]);
    if (son[t][0])rev[son[t][0]] ^= 1;
    if (son[t][1])rev[son[t][1]] ^= 1;
  }

  inline void pushdown(int t) {
    for (stk[++top] = t; t; )
      stk[++top] = t = fat[t];
    for (; top; --top)
      if (rev[stk[top]])
      	push(stk[top]);
  }

  inline void connect(int t, int f, int s) {
    if (t)fat[t] = f;
    if (f)son[f][s] = t;
  }

  inline void rotate(int t) {
    int f = fat[t];
    int g = fat[f];
    int s = son[f][1] == t;
    connect(son[t][!s], f, s);
    connect(f, t, !s);
    fat[t] = g;
    if (g && son[g][0] == f)son[g][0] = t;
    if (g && son[g][1] == f)son[g][1] = t;
    update(f);
    update(t);
  }

  inline void splay(int t) {
    pushdown(t);
    while (!isroot(t)) {
      int f = fat[t];
      int g = fat[f];
      if (isroot(f))
	      rotate(t);
      else {
	      int a = f && son[f][1] == t;
	      int b = g && son[g][1] == f;
	      if (a == b)
	        rotate(f), rotate(t);
	      else
	        rotate(t), rotate(t);
      }
    }
  }

  inline void access(int t) {
    for (int p = 0; t; p = t, t = fat[t])
      splay(t), son[t][1] = p, update(t);
  }

  inline void makeroot(int t) {
    access(t), splay(t), rev[t] ^= 1;
  }

  inline int find(int t) {
    access(t), splay(t);
    while (son[t][0])
      t = son[t][0];
    return t;
  }

  inline void link(int t, int f) {
    makeroot(t), fat[t] = f;
  }

  inline void cut(int a, int b) {
    makeroot(a), access(b), splay(b);
    if (son[b][0] == a)
      son[b][0] = fat[a] = 0, update(b);
  }

  inline void preworkNTR(void) {
    for (int i = 1; i <= n; ++i)
      val[i] = min[i] = inf;
    for (int i = 1; i <= m; ++i) {
      if (E[i].x == E[i].y) {
        ntr[i] = m;
        continue;
      }
      if (find(E[i].x) == find(E[i].y)) {
	      makeroot(E[i].x);
	      access(E[i].y);
	      splay(E[i].y);
	      ntr[i] = min[E[i].y];
	      cut(E[ntr[i]].x, ntr[i] + n);
	      cut(E[ntr[i]].y, ntr[i] + n);
      }
      val[i + n] = min[i + n] = i;
      link(E[i].x, i + n);
      link(E[i].y, i + n);
    }
  }
}

namespace CMT {
  const int K = 25;

  int tot = 0;
  int sum[K * mxn];
  int lsn[K * mxn];
  int rsn[K * mxn];
  int rot[K * mxn];

  void insert(int &t, int p, int l, int r, int v) {
    t = ++tot;
    lsn[t] = lsn[p];
    rsn[t] = rsn[p];
    sum[t] = sum[p] + 1;
    if (l == r)return;
    int mid = (l + r) >> 1;
    if (v <= mid)
      insert(lsn[t], lsn[p], l, mid, v);
    else
      insert(rsn[t], rsn[p], mid + 1, r, v);
  }

  int query(int t, int l, int r, int x, int y) {
    if (!t)return 0;
    if (l == x && r == y)
      return sum[t];
    int mid = (l + r) >> 1;
    if (y <= mid)
      return query(lsn[t], l, mid, x, y);
    else if (x > mid)
      return query(rsn[t], mid + 1, r, x, y);
    else
      return query(lsn[t], l, mid, x, mid) + query(rsn[t], mid + 1, r, mid + 1, y);
  }
  
  inline void preworkNTR(void) {
    for (int i = 1; i <= m; ++i)
      insert(rot[i], rot[i - 1], 0, m, ntr[i]);
  }

  inline int solve(int l, int r) {
    return n - query(rot[r], 0, m, 0, l - 1) + query(rot[l - 1], 0, m, 0, l - 1);
  }
}

signed main(void) {
  n = nextInt();
  m = nextInt();
  q = nextInt();
  e = nextInt();
  for (int i = 1; i <= m; ++i)
    E[i].x = nextInt(),
    E[i].y = nextInt();
  LCT::preworkNTR(); 
  CMT::preworkNTR();
  int lastans = 0;
  for (int i = 1; i <= q; ++i) {
    int L = nextInt();
    int R = nextInt();
    if (e)
      L ^= lastans,
      R ^= lastans;
    printf("%d\n", lastans = CMT::solve(L, R));
  }
}

  

@Author: YouSiki

 

转载于:https://www.cnblogs.com/yousiki/p/6399402.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值