BZOJ 3069 [LCT][并查集][双连通分量]

10 篇文章 0 订阅
9 篇文章 0 订阅

Description

Byteasar是一个很纠结的人。每次他经过Bytetown的时候都知道有至少2条不同的路径可以选择,这导致他必须花很长时间来决定走哪条路。Byteasar最近听说了Bytetown的修路计划,他可能是唯一一个为此感到高兴的人——他有机会消除他的烦恼。
在Byteasar一共有n个岔口,连接着m条双向道路。两条路径完全不同当且仅当他们没有公共的道路(但是允许经过相同的岔口)。
Byteasar想知道:对于两个岔口x y,是否存在一对完全不同的路径。

Solution

%%%Claris
这里写图片描述

可能是自己sb了。。。居然调了那么久,看来以后LCT必须要很熟练的写啊。。。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;

const int N = 301010;
const int INF = 1 << 30;

inline char get(void) {
  static char buf[100000], *S = buf, *T = buf;
  if (S == T) {
    T = (S = buf) + fread(buf, 1, 100000, stdin);
    if (S == T) return EOF; 
  }
  return *S++;
}
inline void read(int &x) {
  static char c; x = 0;
  for (c = get(); c < '0' || c > '9'; c = get());
  for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
}
inline void readopt(int &opt) {
  static char c;
  for (c = get(); c != 'Z' && c != 'P'; c = get());
  opt = (c == 'Z');
}

int n, m, q, Gcnt, x, y, z, cnt;
int a[N], b[N];
int fa[N], rk[N];
int ans[N];
struct Qry {
  int opt, x, y, id;
  inline friend bool operator <(const Qry &a, const Qry &b) {
    return a.id > b.id;
  }
};
Qry Q[N];
vector<int> e[N], g[N];

inline int Fa(int x) {
  return fa[x] == x ? x : fa[x] = Fa(fa[x]);
}
inline int Merge(int x, int y) {
  static int f1, f2;
  f1 = Fa(x); f2 = Fa(y);
  if (f1 == f2) return false;
  if (rk[f1] > rk[f2]) swap(f1, f2);
  if (rk[f1] == rk[f2]) rk[f2]++;
  fa[f1] = f2; return 1;
}

namespace LCT {
  int f[N];
  bool br[N], st[N], sm[N];
  int ch[N][2];
  bool rev[N];
  int sta[N];
  inline bool IsRoot(int x) {
    return ch[f[x]][0] != x && ch[f[x]][1] != x;
  }
  inline void PushDown(int x) {
    if (rev[x]) {
      rev[ch[x][0]] ^= 1; rev[ch[x][1]] ^= 1;
      swap(ch[x][0], ch[x][1]); rev[x] = 0;
    }
    if (st[x]) {
      st[ch[x][0]] = st[ch[x][1]] = 1;
      br[ch[x][0]] = br[ch[x][1]] = 0;
      sm[ch[x][0]] = sm[ch[x][1]] = 0;
      st[x] = 0;
    }
  }
  inline void PushUp(int x) {
    sm[x] = br[x] | sm[ch[x][0]] | sm[ch[x][1]];
  }
  inline void Rotate(int x) {
    int y = f[x], z = f[y],
      l = (ch[y][0] != x), r = l ^ 1;
    if (!IsRoot(y)) {
      if (ch[z][0] == y) ch[z][0] = x;
      else ch[z][1] = x;
    }
    f[x] = z; f[y] = x; f[ch[x][r]] = y;
    ch[y][l] = ch[x][r]; ch[x][r] = y;
    PushUp(y); PushUp(x);
   }
  void Splay(int x) {
    int top = 0; sta[++top] = x;
    for (int i = x; !IsRoot(i); i = f[i])
      sta[++top] = f[i];
    while (top) PushDown(sta[top--]);
    while (!IsRoot(x)) {
      int y = f[x], z = f[y];
      if (!IsRoot(y)) {
        if (ch[y][0] == x ^ ch[z][0] == y) Rotate(x);
        else Rotate(y);
      }
      Rotate(x);
    }
  }
  void Access(int x) {
    for (int t = 0; x; x = f[x]) {
      Splay(x); ch[x][1] = t;
      PushUp(x); t = x;
    }
  }
  void MakeRoot(int x) {
    Access(x); Splay(x); rev[x] ^= 1;
  }
  void Link(int x, int y) {
    MakeRoot(x); f[x] = y;
  }
  void MakePath(int x, int y) {
    MakeRoot(x); Access(y); Splay(y);
  }
  void Cut(int x, int y) {
    MakePath(x, y);
    ch[y][0] = f[x] = 0; PushUp(y);
  }
};
using namespace LCT;

void AddEdge(int x, int y, int z) {
  if (Merge(x, y)) {
    Link(x, z); Link(y, z);
    br[z] = sm[z] = 1;
  } else {
    MakePath(x, y);
    st[y] = 1; sm[y] = br[y] = 0;
  }
}
int Query(int x, int y) {
  if (Fa(x) != Fa(y)) return 1;
  MakePath(x, y); return sm[y];
}

int main(void) {
  read(n); read(m); read(q); cnt = n;
  for (int i = 1; i <= n; i++) {
    fa[i] = i; e[i].push_back(INF);
  }
  for (int i = 1; i <= m; i++) {
    read(x); read(y);
    if (x > y) swap(x, y);
    a[i] = x; b[i] = y;
  }
  for (int i = 1; i <= q; i++) {
    readopt(Q[i].opt);
    read(Q[i].x); read(Q[i].y);
    if (Q[i].x > Q[i].y) swap(Q[i].x, Q[i].y);
    if (Q[i].opt) e[Q[i].x].push_back(Q[i].y);
  }
  for (int i = 1; i <= n; i++)
    sort(e[i].begin(), e[i].end());
  for (int i = 1; i <= m; i++) {
    if (*lower_bound(e[a[i]].begin(), e[a[i]].end(), b[i]) == b[i]) continue;
    AddEdge(a[i], b[i], ++cnt);
  }
  reverse(Q + 1, Q + q + 1);
  for (int i = 1; i <= q; i++) {
    if (Q[i].opt) AddEdge(Q[i].x, Q[i].y, ++cnt);
    else ans[i] = Query(Q[i].x, Q[i].y);
  }
  for (int i = q; i; i--)
    if (!Q[i].opt) printf(ans[i] ? "NIE\n" : "TAK\n");
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值