Problem:
切り取り線(Cutting)
JOI 君はペーパークラフトが趣味である．今日もJOI 君はペーパークラフトの作品を作ろうとしている．まず，JOI 君は設計図にしたがって1 枚の長方形の紙に $N$ 本の切り取り線を印刷した．各切り取り線は，紙の縦または横の辺に平行な線分である．
紙を切り出してできるすべての部分は作品の中で何らかの部品として用いられる．当然のことながら，
部品数の多い作品は製作が大変である．JOI 君は，すべての切り取り線にしたがって紙を切り出したとき，
紙がいくつの部分に分かれるかを知りたい．
課題
紙の大きさと，$N$ 本の切り取り線の情報が与えられる．これらの切り取り線にしたがって紙を切り分けたとき，紙がいくつの部分に分かれるかを求めるプログラムを作成せよ．
入力
標準入力から以下のデータを読み込め．
1 行目には，整数 $W, H, N$ が空白を区切りとして書かれている．$W$ は紙の横の辺の長さを，$H$ は紙の縦の辺の長さを，$N$ は切り取り線の本数をそれぞれ表す．紙の左下，右下，左上，右上を，それぞれ座標 $(0, 0), (W, 0), (0, H), (W, H)$ で表す．
続く $N$ 行のうちの $i$ 行目 $(1 \leq i \leq N)$ には，整数 $A_i, B_i, C_i, D_i (0 \leq A_i \leq C_i \leq W, 0 \leq B_i \leq D_i \leq H)$ が空白を区切りとして書かれている．これは $i$ 番目の切り取り線が$(A_i, B_i)$ と$(C_i, D_i)$ を結ぶ線分であることを表す．この線分は紙のいずれかの辺に平行な線分である．すなわち，$A_i = C_i$ と $B_i = D_i$ のうちのちょうど1 つが成り立つ．また，ある切り取り線と，それに平行な他の切り取り線が共有点を持つことはなく，ある切り取り線と，それに平行な紙の辺が共有点を持つこともない．
出力
標準出力に，紙がいくつの部分に分かれるかを表す整数を1 行で出力せよ．
制限
すべての入力データは以下の条件を満たす．
$ 1 \leq W \leq 1000000 000$
$ 1 \leq H \leq 1000000 000$
$ 1 \leq N \leq 100000$
入出力例
入力例 1
10 10 5
6 0 6 7
0 6 7 6
2 3 9 3
2 3 2 10
1 9 8 9
出力例 1
4
この入力の場合，切り取り線は下図のようになる．
よって，切り取り線によって紙は4 つの部分に分かれる．
入力例 2
13 7 28
1 1 4 1
1 1 1 3
2 2 3 2
2 2 2 3
1 3 2 3
3 2 3 6
4 1 4 6
3 6 4 6
5 1 8 1
5 1 5 6
6 2 7 2
6 2 6 5
7 2 7 5
6 5 7 5
8 1 8 6
5 6 8 6
9 1 12 1
9 1 9 2
9 2 10 2
12 1 12 2
11 2 12 2
10 2 10 5
9 5 10 5
9 5 9 6
11 2 11 5
11 5 12 5
12 5 12 6
9 6 12 6
出力例 2
5
この入力の場合，切り取り線は次ページの図のようになる．
よって，切り取り線によって紙は5 つの部分に分かれる．
問題文と自動審判に使われるデータは、
情報オリンピック日本委員会
が作成し公開している問題文と採点用テストデータです。

Submitted solutions:

code_snippet: #include <bits/stdc++.h>

using namespace std;

const int MAXN = 200200;
using ll = long long;

struct CutLine {
   int xl, yl, xr, yr;
};

int N;
CutLine ls[MAXN];

struct Event {
   int y, op, xl, xr, id;
   Event(int y = 0, int op = 0, int xl = 0, int xr = 0, int id = 0) :
      y(y), op(op), xl(xl), xr(xr), id(id) {}
   bool operator < (const Event &e) const {
      return y < e.y || (y == e.y && op > e.op);
   }
};

int par[MAXN];

int getPar(int v) {
   if (par[v] != v) {
      par[v] = getPar(par[v]);
   }
   return par[v];
}

void merge(int v, int u) {
   par[getPar(u)] = getPar(v);
}

int tr[MAXN << 2];
int lazy[MAXN << 2];

void build(int v, int l, int r) {
   tr[v] = 0;
   lazy[v] = -1;
   if (l < r) {
      int md = (l + r) >> 1;
      build(v << 1, l, md);
      build(v << 1 | 1, md + 1, r);
   }
}

void push(int v) {
   if (~lazy[v]) {
      for (int nv : {v << 1, v << 1 | 1}) {
         if (~lazy[nv]) merge(lazy[nv], lazy[v]);
         if (tr[nv]) lazy[nv] = lazy[v];
      }
      lazy[v] = -1;
   }
}

void modify1(int v, int l, int r, int p, int x) {
   if (l == r) {
      tr[v] += x;
      return;
   }
   int md = (l + r) >> 1;
   if (p <= md) modify1(v << 1, l, md, p, x);
   else modify1(v << 1 | 1, md + 1, r, p, x);
   tr[v] = tr[v << 1] + tr[v << 1 | 1];
}

void modify2(int v, int l, int r, int p, int x) {
   if (l == r) {
      lazy[v] = x;
      return;
   }
   push(v);
   int md = (l + r) >> 1;
   if (p <= md) modify2(v << 1, l, md, p, x);
   else modify2(v << 1 | 1, md + 1, r, p, x);
}

void modify3(int v, int l, int r, int L, int R, int x) {
   if (!tr[v]) return;
   if (L > r || R < l) return;
   if (L <= l && r <= R) {
      if (~lazy[v]) merge(lazy[v], x);
      lazy[v] = x;
      return;
   }
   push(v);
   int md = (l + r) >> 1;
   modify3(v << 1, l, md, L, R, x);
   modify3(v << 1 | 1, md + 1, r, L, R, x);
}

int query(int v, int l, int r, int L, int R) {
   if (L > r || R < l) return 0;
   if (L <= l && r <= R) return tr[v];
   int md = (l + r) >> 1;
   return query(v << 1, l, md, L, R) + query(v << 1 | 1, md + 1, r, L, R);
}

int main() {
   ios_base::sync_with_stdio(false);
   int H, W;
   cin >> H >> W >> N;
   for (int i = 0; i < N; ++i) {
      int a, b, c, d;
      cin >> a >> b >> c >> d;
      ls[i] = {a, b, c, d};
   }
   ls[N++] = {0, 0, 0, W};
   ls[N++] = {0, 0, H, 0};
   ls[N++] = {0, W, H, W};
   ls[N++] = {H, 0, H, W};
   vector<int> xs, ys;
   for (int i = 0; i < N; ++i) {
      xs.emplace_back(ls[i].xl);
      ys.emplace_back(ls[i].yr);
      xs.emplace_back(ls[i].xr);
      ys.emplace_back(ls[i].yr);
   }
   sort(xs.begin(), xs.end());
   xs.resize(unique(xs.begin(), xs.end()) - xs.begin());
   sort(ys.begin(), ys.end());
   ys.resize(unique(ys.begin(), ys.end()) - ys.begin());
   int NX = xs.size(), NY = ys.size();
   vector<Event> evs;
   for (int i = 0; i < N; ++i) {
      int xl = lower_bound(xs.begin(), xs.end(), ls[i].xl) - xs.begin();
      int yl = lower_bound(ys.begin(), ys.end(), ls[i].yl) - ys.begin();
      int xr = lower_bound(xs.begin(), xs.end(), ls[i].xr) - xs.begin();
      int yr = lower_bound(ys.begin(), ys.end(), ls[i].yr) - ys.begin();
      if (xl == xr) {
         evs.emplace_back(yl, +1, xl, xr, i);
         evs.emplace_back(yr, -1, xl, xr, i);
      } else {
         evs.emplace_back(yl, 0, xl, xr, i);
      }
   }
   ll ans = -N;
   build(1, 0, NX - 1);
   iota(par, par + N, 0);
   sort(evs.begin(), evs.end());
   for (auto e : evs) {
      if (e.op == +1) {
         modify2(1, 0, NX - 1, e.xl, e.id);
         modify1(1, 0, NX - 1, e.xl, +1);
      } else if (e.op == 0) {
         modify3(1, 0, NX - 1, e.xl, e.xr, e.id);
         ans += query(1, 0, NX - 1, e.xl, e.xr);
      } else {
         modify2(1, 0, NX - 1, e.xl, -1);
         modify1(1, 0, NX - 1, e.xl, -1);
      }
   }
   for (int i = 0; i < N; ++i) ans += int(par[i] == i);
   cout << ans << "\n";
   return 0;
}
accuracy: 1
time_ms: 170
memory_kb: 18708
final_rank: 4

code_snippet: // AOJ #602
// Cutting 2025.1.27

#include <bits/stdc++.h>
using namespace std;
using ll = long long;

// グローバル変数
int segmentCount, logBase2, treeSize, width, height, componentCount;
int parent[100005];
ll  totalResult;

set<int> activeSegments;

// Union-Find構造のfind関数
int findParent(int x) {
    return parent[x] ? parent[x] = findParent(parent[x]) : x;
}

// 垂直線分と水平線分の構造体
struct Segment {
    union { int x, y; };
    int start, end;
} verticalSegments[100005], horizontalSegments[100005];

// セグメントツリーのノード
struct SegmentTreeNode {
    int value, tag;
} segmentTree[1 << 19];

// セグメントツリーの左・右子ノードのインデックス取得関数
inline int leftChild(int index) { return index << 1; }
inline int rightChild(int index) { return index << 1 | 1; }

// セグメントツリーのノード値を取得
int getNodeValue(int index) {
    return segmentTree[index].tag = -1, segmentTree[index].value;
}

// セグメントツリーの更新（値を加算）
void updateTreeValue(int index, int delta) {
    for (index += treeSize; index; index >>= 1) segmentTree[index].value += delta;
}

// セグメントツリーの更新（タグ付け）
void updateTreeTag(int index, int tagValue) {
    for (int i = logBase2, hasTag = 0; ~i; hasTag |= segmentTree[(index + treeSize) >> i].tag,
         segmentTree[(index + treeSize) >> i].tag = 0, i--) {
        if (hasTag) segmentTree[((index + treeSize) >> i) ^ 1].tag = -1;
    }
    segmentTree[index + treeSize].tag = tagValue;
}

// 範囲クエリ処理
void rangeQuery(int left, int right) {
    if (left <= right) {
        for (left += treeSize - 1, right += treeSize + 1; left ^ right ^ 1; left >>= 1, right >>= 1) {
            if ((left & 1) == 0) totalResult += getNodeValue(left ^ 1);
            if ((right & 1) == 1) totalResult += getNodeValue(right ^ 1);
        }
    }
}

// タグを確認しながらクエリを実行
int queryWithTag(int index, int result = 0) {
    for (result = segmentTree[index += treeSize].tag; index >>= 1;) {
        if (segmentTree[index].tag) return -1;
    }
    return ~result ? findParent(result) : -1;
}

// 座標変換用の配列と変換関数
int xCoordinates[200005], yCoordinates[200005], uniqueXCount, uniqueYCount;

int mapXCoordinate(int x) {
    return lower_bound(xCoordinates + 1, xCoordinates + uniqueXCount + 1, x) - xCoordinates;
}

int mapYCoordinate(int y) {
    return lower_bound(yCoordinates + 1, yCoordinates + uniqueYCount + 1, y) - yCoordinates;
}

// イベント構造体とヘッド配列
struct Event {
    int type, x, y, next;
} events[200005];

int eventHead[200005];

// イベント挿入
void insertSegment(int x) {
    int prevSegment = *--activeSegments.insert(x).first;
    int prevComponent = queryWithTag(prevSegment);
    totalResult--;
    if (prevComponent == -1) prevComponent = ++componentCount, updateTreeTag(prevSegment, prevComponent);
    updateTreeValue(x, 1), updateTreeTag(x, prevComponent);
}

// イベント削除
void eraseSegment(int x) {
    int prevSegment = *--activeSegments.erase(activeSegments.find(x));
    int prevComponent1 = queryWithTag(prevSegment), prevComponent2 = queryWithTag(x);
    updateTreeValue(x, -1);
    if (prevComponent1 != -1) {
        if (prevComponent1 == prevComponent2) totalResult++;
        else if (prevComponent2 != -1) parent[prevComponent1] = prevComponent2;
    } else if (prevComponent2 != -1) updateTreeTag(prevSegment, prevComponent2);
}

// メイン関数
signed main() {
    ios::sync_with_stdio(0);
    cin.tie(0);

    cin >> width >> height >>segmentCount;
    uniqueXCount = uniqueYCount = 2;

    int verticalCount = 0, horizontalCount = 2;
    horizontalSegments[1] = {0, 0, width};
    horizontalSegments[2] = {height, 0, width};
    xCoordinates[2] = width, yCoordinates[2] = height;

    for (int i = 1; i <= segmentCount; i++) {
        int x0, y0, x1, y1;
        cin >> x0 >> y0 >> x1 >> y1;
        if (x0 == x1) verticalSegments[++verticalCount] = {x0, y0, y1}, yCoordinates[++uniqueYCount] = y1;
        else horizontalSegments[++horizontalCount] = {y0, x0, x1}, xCoordinates[++uniqueXCount] = x1;
        xCoordinates[++uniqueXCount] = x0, yCoordinates[++uniqueYCount] = y0;
    }

    sort(xCoordinates + 1, xCoordinates + uniqueXCount + 1);
    sort(yCoordinates + 1, yCoordinates + uniqueYCount + 1);
    uniqueXCount = unique(xCoordinates + 1, xCoordinates + uniqueXCount + 1) - xCoordinates - 1;
    uniqueYCount = unique(yCoordinates + 1, yCoordinates + uniqueYCount + 1) - yCoordinates - 1;

    logBase2 = __lg(uniqueXCount + 1) + 1;
    treeSize = 1 << logBase2;
    updateTreeValue(1, 1);
    segmentTree[1].tag = -1;
    activeSegments = {0, 1, uniqueXCount};

    for (int i = 1; i <= verticalCount; i++) {
        verticalSegments[i] = {mapXCoordinate(verticalSegments[i].x),
            mapYCoordinate(verticalSegments[i].start), mapYCoordinate(verticalSegments[i].end)};
    }

    for (int i = 1; i <= verticalCount; i++) {
        events[++componentCount] = {-1, verticalSegments[i].x, 0, eventHead[verticalSegments[i].end]};
        eventHead[verticalSegments[i].end] = componentCount;
    }

    for (int i = 1; i <= horizontalCount; i++) {
        int mappedY = mapYCoordinate(horizontalSegments[i].y);
        events[++componentCount] = {0, mapXCoordinate(horizontalSegments[i].start), 
            mapXCoordinate(horizontalSegments[i].end), eventHead[mappedY]};
        eventHead[mappedY] = componentCount;
    }

    for (int i = 1; i <= verticalCount; i++) {
        events[++componentCount] = {+1, verticalSegments[i].x, 0, eventHead[verticalSegments[i].start]};
        eventHead[verticalSegments[i].start] = componentCount;
    }

    componentCount = 0;
    for (int i = 1; i <= uniqueYCount; i++) {
        for (int j = eventHead[i]; j; j = events[j].next) {
            switch (events[j].type) {
                case  1: insertSegment(events[j].x); break;
                case -1: eraseSegment(events[j].x); break;
                case  0: rangeQuery(events[j].x, *----activeSegments.upper_bound(events[j].y)); break;
            }
        }
    }
    printf("%lld\n", totalResult - 1);
    return 0;
}
accuracy: 1
time_ms: 100
memory_kb: 17936
final_rank: 1

code_snippet: #include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> Point;
enum : int { CREATEY, APPEARX, DELETEY };

struct Event {
  int y, idx, type;
  bool operator<(const Event& e) const {
    if (y == e.y) return (type < e.type);
    return (y < e.y);
  }
};

struct BinaryIndexedTree {
  vector<int> data;
  BinaryIndexedTree(int sz) {
    data.assign(sz + 2, 0);
  }
  int query(int k) {
    int ret = 0;
    for (++k; k > 0; k -= k & -k) ret += data[k];
    return ret;
  }
  void update(int k, int x) {
    for (++k; k < data.size(); k += k & -k) data[k] += x;
  }
};

struct SegmentTree {
  vector<int> isnew;
  int sz;
  SegmentTree(int n) {
    sz = 1;
    while (sz < n) sz <<= 1;
    isnew.assign(2 * sz - 1, false);
  }
  inline void AddF(const int a, const int b, int k, int l, int r) {
    if (a >= r || b <= l) {
      return;
    } else if (a <= l && r <= b) {
      isnew[k] = true;
    } else {
      AddF(a, b, 2 * k + 1, l, (l + r) >> 1);
      AddF(a, b, 2 * k + 2, (l + r) >> 1, r);
    }
  }
  inline void DelF(const int a, const int b, int k, int l, int r) {
    if (a >= r || b <= l) {
      return;
    } else if (a <= l && r <= a + 1) {
      isnew[k] = false;
    } else {
      if (isnew[k]) {
        isnew[2 * k + 1] = true;
        isnew[2 * k + 2] = true;
        isnew[k] = false;
      }
      DelF(a, b, 2 * k + 1, l, (l + r) >> 1);
      DelF(a, b, 2 * k + 2, (l + r) >> 1, r);
    }
  }
  inline void AddF(int a, int b) {
    AddF(a, b, 0, 0, sz);
  }
  inline void DelF(const int x) {
    DelF(x, x + 1, 0, 0, sz);
  }
  inline bool CheckF(int k) {
    k += sz - 1;
    if (isnew[k]) return true;
    while (k > 0) {
      k = (k - 1) >> 1;
      if (isnew[k]) return true;
    }
    return false;
  }
};

struct UnionFind {
  vector<int> data;
  UnionFind() {};
  inline int Make() {
    data.push_back(-1);
    return(data.size() - 1);
  }
  inline bool Unite(int a, int b) {
    a = Find(a), b = Find(b);
    if (a == b) return false;
    if (data[a] > data[b]) swap(a, b);
    data[a] += data[b];
    data[b] = a;
    return true;
  }
  inline int Find(int k) {
    if (data[k] < 0) return k;
    else return data[k] = Find(data[k]);
  }
};

SegmentTree* tree;
UnionFind* uf;
BinaryIndexedTree* bit;
int decode[200020];

int Evaluate(int x) {
  if (tree->CheckF(x)) {
    tree->DelF(x);
    return uf->Make();
  } else {
    return decode[x];
  }
}

int Press(int* A, int sz1, int* B, int sz2) {
  vector<int> nums;
  copy(A, A + sz1, back_inserter(nums));
  copy(B, B + sz2, back_inserter(nums));
  nums.push_back(-1);
  sort(nums.begin(), nums.end());
  nums.erase(unique(nums.begin(), nums.end()), nums.end());
  transform(A, A + sz1, A, [&](int x) {
    return(lower_bound(nums.begin(), nums.end(), x) - nums.begin());
  });
  transform(B, B + sz2, B, [&](int x) {
    return(lower_bound(nums.begin(), nums.end(), x) - nums.begin());
  });
  return(nums.size());
}

int main() {
  int W, H, N;
  int A[100004], B[100004], C[100004], D[100004];

  // 入力
  scanf("%d %d %d", &W, &H, &N);
  for (int i = 0; i < N; i++) {
    scanf("%d %d %d %d", &A[i], &B[i], &C[i], &D[i]);
  }

  // 追加の境界線を追加
  A[N] = 0; B[N] = 0; C[N] = W; D[N] = 0; ++N;
  A[N] = 0; B[N] = 0; C[N] = 0; D[N] = H; ++N;
  A[N] = W; B[N] = 0; C[N] = W; D[N] = H; ++N;
  A[N] = 0; B[N] = H; C[N] = W; D[N] = H; ++N;

  W = Press(A, N, C, N);

  vector<Event> Events;

  // イベントの生成
  for (int i = 0; i < N; i++) {
    if (A[i] == C[i]) {
      Events.push_back((Event) { B[i], i, CREATEY });
      Events.push_back((Event) { D[i], i, DELETEY });
    } else {
      Events.push_back((Event) { B[i], i, APPEARX });
    }
  }
  sort(Events.begin(), Events.end());

  set<int> pool;
  uf = new UnionFind();
  tree = new SegmentTree(W);
  bit = new BinaryIndexedTree(W);
  pool.insert(0); decode[0] = uf->Make();
  long long int sz = 0;

  // イベントの処理
  for (int i = 0; i < Events.size(); i++) {
    const Event& e = Events[i];
    const int& x = A[e.idx];
    if (e.type == CREATEY) {
      int L = *--pool.lower_bound(x);
      decode[x] = Evaluate(x);
      decode[L] = Evaluate(L);
      decode[x] = decode[L];
      pool.insert(x);
      bit->update(x, 1);
    } else if (e.type == DELETEY) {
      int L = *--pool.lower_bound(x);
      decode[x] = Evaluate(x);
      decode[L] = Evaluate(L);
      if (uf->Unite(decode[x], decode[L])) --sz;
      pool.erase(x);
      bit->update(x, -1);
    } else {
      int x2 = C[e.idx];
      int RSQ = bit->query(x2) - bit->query(x - 1) - 1;
      if (RSQ > 0) {
        sz += RSQ;
        tree->AddF(x, *--pool.upper_bound(x2));
      }
    }
  }
  printf("%lld\n", sz);
}
accuracy: 1
time_ms: 150
memory_kb: 13960
final_rank: 3

code_snippet: #include <bits/stdc++.h>
using namespace std;
#define FOR(i,m,n) for(int i=(m);i<(n);++i)
#define REP(i,n) FOR(i,0,n)
#define ALL(v) (v).begin(),(v).end()
using ll = long long;
constexpr int INF = 0x3f3f3f3f;
constexpr long long LINF = 0x3f3f3f3f3f3f3f3fLL;
constexpr double EPS = 1e-8;
constexpr int MOD = 998244353;
// constexpr int MOD = 1000000007;
constexpr int DY4[]{1, 0, -1, 0}, DX4[]{0, -1, 0, 1};
constexpr int DY8[]{1, 1, 0, -1, -1, -1, 0, 1};
constexpr int DX8[]{0, -1, -1, -1, 0, 1, 1, 1};
template <typename T, typename U>
inline bool chmax(T& a, U b) { return a < b ? (a = b, true) : false; }
template <typename T, typename U>
inline bool chmin(T& a, U b) { return a > b ? (a = b, true) : false; }
struct IOSetup {
  IOSetup() {
    std::cin.tie(nullptr);
    std::ios_base::sync_with_stdio(false);
    std::cout << fixed << setprecision(20);
  }
} iosetup;

template <typename Abelian>
struct FenwickTree {
  explicit FenwickTree(const int n, const Abelian ID = 0)
      : n(n), ID(ID), data(n, ID) {}

  void add(int idx, const Abelian val) {
    for (; idx < n; idx |= idx + 1) {
      data[idx] += val;
    }
  }

  Abelian sum(int idx) const {
    Abelian res = ID;
    for (--idx; idx >= 0; idx = (idx & (idx + 1)) - 1) {
      res += data[idx];
    }
    return res;
  }

  Abelian sum(const int left, const int right) const {
    return left < right ? sum(right) - sum(left) : ID;
  }

  Abelian operator[](const int idx) const { return sum(idx, idx + 1); }

  int lower_bound(Abelian val) const {
    if (val <= ID) return 0;
    int res = 0, exponent = 1;
    while (exponent <= n) exponent <<= 1;
    for (int mask = exponent >> 1; mask > 0; mask >>= 1) {
      const int idx = res + mask - 1;
      if (idx < n && data[idx] < val) {
        val -= data[idx];
        res += mask;
      }
    }
    return res;
  }

 private:
  const int n;
  const Abelian ID;
  std::vector<Abelian> data;
};

struct UnionFind {
  explicit UnionFind(const int n) : data(n, -1) {}

  int root(const int ver) {
    return data[ver] < 0 ? ver : data[ver] = root(data[ver]);
  }

  bool unite(int u, int v) {
    u = root(u);
    v = root(v);
    if (u == v) return false;
    if (data[u] > data[v]) std::swap(u, v);
    data[u] += data[v];
    data[v] = u;
    return true;
  }

  bool is_same(const int u, const int v) { return root(u) == root(v); }

  int size(const int ver) { return -data[root(ver)]; }

 private:
  std::vector<int> data;
};

int main() {
  int w, h, n; cin >> w >> h >> n;
  vector<int> a(n + 4), b(n + 4), c(n + 4), d(n + 4); REP(i, n) cin >> a[i] >> b[i] >> c[i] >> d[i];
  a[n] = b[n] = d[n] = 0; c[n] = w;
  a[n + 1] = b[n + 1] = c[n + 1] = 0; d[n + 1] = h;
  a[n + 2] = c[n + 2] = w; b[n + 2] = 0; d[n + 2] = h;
  a[n + 3] = 0; b[n + 3] = d[n + 3] = h; c[n + 3] = w;
  vector<int> xs{0, w};
  xs.reserve((n + 4) * 6 + 2);
  copy(ALL(a), back_inserter(xs));
  REP(i, n + 4) {
    if (a[i] > 0) xs.emplace_back(a[i] - 1);
    if (a[i] + 1 <= w) xs.emplace_back(a[i] + 1);
  }
  copy(ALL(c), back_inserter(xs));
  REP(i, n + 4) {
    if (c[i] > 0) xs.emplace_back(c[i] - 1);
    if (c[i] + 1 <= w) xs.emplace_back(c[i] + 1);
  }
  sort(ALL(xs));
  xs.erase(unique(xs.begin(), xs.end()), xs.end());
  const int x_size = xs.size();
  vector<int> ys{0, h};
  ys.reserve((n + 4) * 6 + 2);
  copy(ALL(b), back_inserter(ys));
  REP(i, n + 4) {
    if (b[i] > 0) ys.emplace_back(b[i] - 1);
    if (b[i] + 1 <= h) ys.emplace_back(b[i] + 1);
  }
  copy(ALL(d), back_inserter(ys));
  REP(i, n + 4) {
    if (d[i] > 0) ys.emplace_back(d[i] - 1);
    if (d[i] + 1 <= h) ys.emplace_back(d[i] + 1);
  }
  sort(ALL(ys));
  ys.erase(unique(ys.begin(), ys.end()), ys.end());
  const int y_size = ys.size();
  REP(i, n + 4) a[i] = distance(xs.begin(), lower_bound(ALL(xs), a[i]));
  REP(i, n + 4) b[i] = distance(ys.begin(), lower_bound(ALL(ys), b[i]));
  REP(i, n + 4) c[i] = distance(xs.begin(), lower_bound(ALL(xs), c[i]));
  REP(i, n + 4) d[i] = distance(ys.begin(), lower_bound(ALL(ys), d[i]));
  vector<vector<pair<int, int>>> par_y(x_size), ins_x(x_size), era_x(x_size);
  REP(i, n + 4) {
    if (a[i] == c[i]) {
      par_y[a[i]].emplace_back(b[i], d[i]);
    } else {
      ins_x[a[i]].emplace_back(b[i], i);
      era_x[c[i]].emplace_back(b[i], i);
    }
  }

  ll ans = -(n + 4);

  FenwickTree<int> bit(y_size);
  REP(x, x_size) {
    for (const auto& [y, _] : ins_x[x]) bit.add(y, 1);
    for (const auto& [y1, y2] : par_y[x]) {
      const int cross = bit.sum(y1, y2 + 1);
      ans += cross;
      if (cross == 0) ++ans;
    }
    for (const auto& [y, _] : era_x[x]) bit.add(y, -1);
  }

  UnionFind union_find(n + 4);
  vector<int> id_y(y_size, -1);
  set<int> t;
  map<int, int> intervals;
  const auto find = [&](const int y) {
    const auto it = intervals.lower_bound(y);
    if (it != intervals.end() && it->first == y) return it;
    if (it != intervals.begin() && prev(it)->first <= y && y <= prev(it)->second) return prev(it);
    return intervals.end();
  };
  REP(x, x_size) {
    for (const auto [y, id] : ins_x[x]) {
      if (const auto it = find(y); it != intervals.end()) {
        const auto [l, r] = *it;
        intervals.erase(it);
        const auto it_r = t.lower_bound(y);
        if (it_r != t.end() && *it_r <= r) {
          assert(intervals.emplace(*it_r, r).second);
        }
        if (it_r != t.begin() && *prev(it_r) >= l) {
          assert(intervals.emplace(l, *prev(it_r)).second);
        }
        assert(intervals.emplace(y, y).second);
      } else {
        assert(intervals.emplace(y, y).second);
      }
      id_y[y] = id;
      assert(t.emplace(y).second);
    }
    for (const auto& [y1, y2] : par_y[x]) {
      auto it = find(y1);
      if (it == intervals.end()) {
        it = intervals.lower_bound(y1);
        if (it != intervals.end() && it->first > y2) it = intervals.end();
      }
      if (it != intervals.end()) {
        vector<pair<int, int>> segs;
        for (; it != intervals.end() && it->first <= y2; it = intervals.erase(it)) {
          segs.emplace_back(*it);
        }
        FOR(i, 1, segs.size()) {
          union_find.unite(id_y[segs[i - 1].first], id_y[segs[i].first]);
        }
        assert(intervals.emplace(segs.front().first, segs.back().second).second);
      }
    }
    for (const auto& [y, _] : era_x[x]) {
      id_y[y] = -1;
      assert(t.erase(y) > 0);
      const auto it = find(y);
      assert(it != intervals.end());
      const auto [l, r] = *it;
      intervals.erase(it);
      const auto it_r = t.lower_bound(y);
      if (it_r != t.end() && *it_r <= r) {
        assert(intervals.emplace(*it_r, r).second);
      }
      if (it_r != t.begin() && *prev(it_r) >= l) {
        assert(intervals.emplace(l, *prev(it_r)).second);
      }
    }
  }
  REP(i, n + 4) {
    if (b[i] == d[i] && union_find.root(i) == i) ++ans;
  }
  cout << ans << '\n';
  return 0;
}
accuracy: 1
time_ms: 210
memory_kb: 51936
final_rank: 5

code_snippet: #include <bits/stdc++.h>
#define err(args...) {}
#ifdef DEBUG
#include "_debug.cpp"
#endif
using namespace std;
using ll = long long;
using ld = long double;
template <typename T> using lim = numeric_limits<T>;
template <typename T> istream& operator>>(istream& is, vector<T>& a) { for(T& x : a) { is >> x; } return is; }
template <typename X, typename Y> istream& operator>>(istream& is, pair<X, Y>& p) { return is >> p.first >> p.second; }
template <typename T> struct monoid {
    const T identity;
    const function<T(T, T)> combine;
};
template <typename In, typename Out = In, typename Node = In> struct segtree {
	const int size;
	vector<Node> tree;
    const monoid<Out> m;
    template <typename Leaf, typename Branch>
	segtree(const vector<In>& a, const monoid<Out>& m, const Leaf& leaf, const Branch& branch) 
    : size(a.size()), tree(2 * (size + 1)), m(m) {
        static_assert(is_convertible<decltype(leaf), function<Node(const In&)>>::value);
        static_assert(is_convertible<decltype(branch), function<Node(const Node&, const Node&)>>::value);
		for(int i = 0; i < size; i++) tree[i + size] = leaf(a[i]);
		for(int i = size - 1; i > 0; i--) tree[i] = branch(tree[i << 1], tree[(i << 1) | 1]);
    }
	static constexpr auto identity = [](const auto& x) { return x; };
    segtree(const vector<In>& a, const monoid<Out>& m) : segtree(a, m, identity, m.combine) {}
	template <typename Query = decltype(identity)> 
    Out query(int L, int R, bool inclusive, const Query& query_f = identity) {
        static_assert(is_convertible<decltype(query_f), function<Out(Node&)>>::value);
		assert(L <= R + inclusive);
		Out ans_l = m.identity, ans_r = m.identity;
		for(int a = L + size, b = R + size + inclusive; a < b; a >>= 1, b >>= 1) {
			if(a & 1) ans_l = m.combine(ans_l, query_f(tree[a++]));
			if(b & 1) ans_r = m.combine(query_f(tree[--b]), ans_r);
		}
		return m.combine(ans_l, ans_r);
	}
    template <typename Query = decltype(identity)> 
    Out query(int i, const Query& query_f = identity) { return query(i, i, true, query_f); }
    void update(int i, const In& new_value) {
        static_assert(is_same<Node, In>::value);
        static_assert(is_same<Node, Out>::value);
        for(tree[i += size] = new_value; i >>= 1; )
            tree[i] = m.combine(tree[i << 1], tree[(i << 1) | 1]);
    }
    template <typename UpdateNode> void update(int i, const In& new_value, const UpdateNode& update_node) {
        static_assert(is_convertible<decltype(update_node), function<void(Node&, const In&)>>::value);
        for(update_node(tree[i += size], new_value); i >>= 1; )
            update_node(tree[i], new_value);
    }
    template <typename Leaf, typename Branch> void update(int i, const In& new_value, const Leaf& leaf, const Branch& branch) {
        static_assert(is_convertible<decltype(leaf), function<void(Node&, const In&)>>::value);
        static_assert(is_convertible<decltype(branch), function<void(Node&, const Node&, const Node&, const In&)>>::value);
        for(leaf(tree[i += size], new_value); i >>= 1; )
            branch(tree[i], tree[i << 1], tree[(i << 1) | 1], new_value);
    }
};
struct ufds {
    vector<int> p;
    ufds(int n) : p(n + 1) {
        iota(p.begin(), p.end(), 0);
    }
    int find(int u) {
        return p[u] == u ? u : p[u] = find(p[u]);
    }
    void onion(int u, int v) {
        if(find(u) != find(v)) {
            p[find(u)] = find(v);
        }
    }
};
const int N = 100'000, vert = 0, hori = 1;
int coords_for_axis[2][N+4][2];
void compress(int n, int coords[N+4][2]) {
    vector<int> vals;
    for(int i = 0; i < n + 4; i++) {
        for(int val : coords[i]) {
            vals.push_back(val);
        }
    }
    sort(vals.begin(), vals.end());
    vals.erase(unique(vals.begin(), vals.end()), vals.end());
    for(int i = 0; i < n + 4; i++) {
        for(int& val : coords[i]) {
            val = lower_bound(vals.begin(), vals.end(), val) - vals.begin() + 1;
        }
    }
}
enum class event_type { add, query, remove };
struct event {
    event_type type;
    int id;
    int position(int axis) const {
        if(type == event_type::add) {
            return coords_for_axis[axis][id][0];
        } else if(type == event_type::remove) {
            return coords_for_axis[axis][id][1];
        } else {
            return coords_for_axis[axis][id][0];
        }
    }
    pair<int, event_type> ordering(int axis) const {
        return {position(axis ^ 1), type};
    };
};
int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(0);
    int w, h, n;
    cin >> w >> h >> n;
    vector<vector<event>> sweep_events_for_axis(2);
    auto insert = [&](int x0, int y0, int x1, int y1, int i) {
        coords_for_axis[hori][i][0] = x0, coords_for_axis[vert][i][0] = y0;
        coords_for_axis[hori][i][1] = x1, coords_for_axis[vert][i][1] = y1;
        if(coords_for_axis[hori][i][0] == coords_for_axis[hori][i][1]) {
            sweep_events_for_axis[vert].push_back({event_type::query, i});
            sweep_events_for_axis[hori].push_back({event_type::add, i});
            sweep_events_for_axis[hori].push_back({event_type::remove, i});
        } else {
            sweep_events_for_axis[hori].push_back({event_type::query, i});
            sweep_events_for_axis[vert].push_back({event_type::add, i});
            sweep_events_for_axis[vert].push_back({event_type::remove, i});
        }
    };
    for(int i = 0; i < n; i++) {
        int x0, y0, x1, y1;
        cin >> x0 >> y0 >> x1 >> y1;
        insert(x0, y0, x1, y1, i);
    }
    insert(0, 0, w, 0, n);
    insert(0, h, w, h, n + 1);
    insert(0, 0, 0, h, n + 2);
    insert(w, 0, w, h, n + 3);
    for(auto& coords : coords_for_axis) {
        compress(n, coords);
    }
    ll intersection_vertices = 0, non_intersection_vertices = 0;
    vector<ll> edges_along(n + 4);
    vector<ufds> uf(2, ufds(n + 4));
    for(int axis : {vert, hori}) {
        segtree<int> rsq(vector<int>(2 * n + 2), {0, plus<int>()});
        segtree<int, int, set<int>> st(
            vector<int>(2 * n + 2), 
            {0, plus<int>()}, 
            [](int) { return set<int>(); }, 
            [](const set<int>&, const set<int>&) { return set<int>(); }
        );
        auto node_insert = [](set<int>& s, int x) { s.insert(x); };
        auto node_erase = [](set<int>& s, int x) { s.erase(x); };
        sort(sweep_events_for_axis[axis].begin(), sweep_events_for_axis[axis].end(), [&](const event& e1, const event& e2)  {
            return e1.ordering(axis) < e2.ordering(axis);
        });
        for(event& e : sweep_events_for_axis[axis]) {
            if(e.type == event_type::add) {
                rsq.update(e.position(axis), 1);
                st.update(e.position(axis), e.id, node_insert);
            } else if(e.type == event_type::remove) {
                rsq.update(e.position(axis), 0);
                st.update(e.position(axis), e.id, node_erase);
            } else {
                int v0 = coords_for_axis[axis][e.id][0], v1 = coords_for_axis[axis][e.id][1];
                int curr_intersection_vertices = rsq.query(v0, v1, true);
                int curr_non_intersection_vertices = (rsq.query(v0) == 0) + (rsq.query(v1) == 0);
                intersection_vertices += curr_intersection_vertices;
                non_intersection_vertices += curr_non_intersection_vertices;
                edges_along[e.id] = curr_intersection_vertices + curr_non_intersection_vertices - 1;
                st.query(v0, v1, true, [&](set<int>& s) {
                    int max_cross = -1;
                    for(int cross : s) {
                        uf[axis].onion(e.id, cross);
                        if(max_cross == -1 or coords_for_axis[axis ^ 1][cross][1] > coords_for_axis[axis ^ 1][max_cross][1]) {
                            max_cross = cross;
                        }
                    }
                    s.clear();
                    if(max_cross != -1) {
                        s.insert(max_cross);
                    }
                    return 0;
                });
            }
        }
    }
    ll vertices = intersection_vertices / 2 + non_intersection_vertices;
    ll edges = accumulate(edges_along.begin(), edges_along.end(), 0ll);
    ll components = 0;
    for(int i = 0; i < n + 4; i++) {
        components += i == uf[0].p[i];
    }
    cout << components - vertices + edges << endl;
    return 0;
}
accuracy: 1
time_ms: 930
memory_kb: 72616
final_rank: 10

code_snippet: #include <iostream>
#include <vector>
#include <set>
#include <cmath>
#include <iterator>
#include <algorithm>
using namespace std;
#pragma warning (disable: 4996)

// -------------------------------------------------- ライブラリ 1（非自明なやつ） ---------------------------------------------------
const int MAX_N = (1 << 19);

class SuperSegmentTree {
public:
	int size_ = 1;
	int A[MAX_N];
	set<int> dat[MAX_N * 2], idx[MAX_N];
	vector<int> I;

	void init(int sz) {
		while (size_ <= sz) size_ *= 2;
		for (int i = 0; i <= sz; i++) A[i] = 0;
	}
	void dels(int pos, int x) {
		idx[x].erase(pos); pos += size_;
		dat[pos].erase(x);
		while (pos >= 1) {
			auto itr1 = dat[pos * 2 + 0].lower_bound(x);
			auto itr2 = dat[pos * 2 + 1].lower_bound(x);
			int cntv = 0;
			if (itr1 == dat[pos * 2 + 0].end() || (*itr1) != x) cntv++;
			if (itr2 == dat[pos * 2 + 1].end() || (*itr2) != x) cntv++;
			if (cntv == 2) dat[pos].erase(x);
			pos >>= 1;
		}
	}
	void adds(int pos, int x) {
		idx[x].insert(pos); pos += size_;
		while (pos >= 1) {
			dat[pos].insert(x);
			pos >>= 1;
		}
	}
	void update(int pos, int x) {
		if (A[pos] != 0) {
			dels(pos, A[pos]);
			A[pos] = 0;
		}
		if (x != 0) {
			A[pos] = x;
			adds(pos, A[pos]);
		}
	}
	bool check(int x) {
		if (idx[x].size() >= 1) return true;
		return false;
	}
	int reads(int pos) {
		return A[pos];
	}
	void query(int l, int r, int a, int b, int u) {
		if (l <= a && b <= r) {
			auto itr = dat[u].begin();
			while (itr != dat[u].end()) { I.push_back(*itr); itr++; }
			return;
		}
		if (r <= a || b <= l) return;
		query(l, r, a, (a + b) >> 1, u * 2);
		query(l, r, (a + b) >> 1, b, u * 2 + 1);
	}
	void conquer(int cl, int cr) {
		I.clear();
		query(cl, cr, 0, size_, 1);
		sort(I.begin(), I.end());
		I.erase(unique(I.begin(), I.end()), I.end());

		vector<pair<int, int>> Z;
		for (int i : I) Z.push_back(make_pair(idx[i].size(), i));
		sort(Z.begin(), Z.end());

		for (int i = 0; i < (int)Z.size() - 1; i++) {
			int pos = Z[i].second; vector<int> vecs;
			auto itr = idx[pos].begin();
			while (itr != idx[pos].end()) { vecs.push_back(*itr); itr++; }
			for (int j : vecs) update(j, Z[Z.size() - 1].second);
		}
		return;
	}
};

// --------------------------------------------------- ライブラリ 2（自明なやつ） ----------------------------------------------------
class BIT {
public:
	int size_ = 1;
	vector<int> bit;

	void init(int sz) {
		size_ = sz + 2;
		bit.resize(size_ + 2, 0);
	}
	void add(int pos, int x) {
		pos++;
		while (pos <= size_) {
			bit[pos] += x;
			pos += (pos & -pos);
		}
	}
	int sum(int pos) {
		pos++; int s = 0;
		while (pos >= 1) {
			s += bit[pos];
			pos -= (pos & -pos);
		}
		return s;
	}
};

struct Point {
	int px, py;
};

bool operator<(const Point& a1, const Point& a2) {
	if (a1.px < a2.px) return true;
	if (a1.px > a2.px) return false;
	if (a1.py < a2.py) return true;
	return false;
}

bool operator==(const Point& a1, const Point& a2) {
	if (a1.px == a2.px && a1.py == a2.py) return true;
	return false;
}

class UnionFind {
public:
	vector<int> par;

	void init(int sz) {
		par.resize(sz, -1);
	}
	int root(int pos) {
		if (par[pos] == -1) return pos;
		par[pos] = root(par[pos]);
		return par[pos];
	}
	void unite(int u, int v) {
		u = root(u); v = root(v);
		if (u == v) return;
		par[u] = v;
	}
	bool same(int u, int v) {
		if (root(u) == root(v)) return true;
		return false;
	}
};

// -------------------------------------------- 本質 -----------------------------------------------
int H, W, N;
Point G1[1 << 17], G2[1 << 17];
vector<int> V[1 << 18][3];
set<int> Set;
SuperSegmentTree S;

int main() {
	// ステップ 1. 入力
	scanf("%d%d%d", &W, &H, &N);
	for (int i = 1; i <= N; i++) scanf("%d%d%d%d", &G1[i].px, &G1[i].py, &G2[i].px, &G2[i].py);
	G1[N + 1] = Point{ 0, 0 }; G2[N + 1] = Point{ W,0 };
	G1[N + 2] = Point{ 0, H }; G2[N + 2] = Point{ W,H };
	G1[N + 3] = Point{ 0, 0 }; G2[N + 3] = Point{ 0,H };
	G1[N + 4] = Point{ W, 0 }; G2[N + 4] = Point{ W,H };
	N += 4;

	// ステップ 2. 座標圧縮
	vector<int> LX, LY;
	for (int i = 1; i <= N; i++) {
		LX.push_back(G1[i].px); LY.push_back(G1[i].py);
		LX.push_back(G2[i].px); LY.push_back(G2[i].py);
	}
	sort(LX.begin(), LX.end()); LX.erase(unique(LX.begin(), LX.end()), LX.end());
	sort(LY.begin(), LY.end()); LY.erase(unique(LY.begin(), LY.end()), LY.end());
	for (int i = 1; i <= N; i++) {
		G1[i].px = lower_bound(LX.begin(), LX.end(), G1[i].px) - LX.begin();
		G1[i].py = lower_bound(LY.begin(), LY.end(), G1[i].py) - LY.begin();
		G2[i].px = lower_bound(LX.begin(), LX.end(), G2[i].px) - LX.begin();
		G2[i].py = lower_bound(LY.begin(), LY.end(), G2[i].py) - LY.begin();
		if (G1[i].px > G2[i].px) swap(G1[i].px, G2[i].px);
		if (G1[i].py > G2[i].py) swap(G1[i].py, G2[i].py);
	}
	W = LX.size() - 1;
	H = LY.size() - 1;

	// ステップ 3. 頂点個数の数え上げ（準備編）
	for (int i = 1; i <= N; i++) {
		if (G1[i].px == G2[i].px) {
			V[G1[i].px][1].push_back(i);
		}
		else {
			V[G1[i].px][0].push_back(i);
			V[G2[i].px][2].push_back(i);
		}
	}

	long long Vertex = 0;
	long long Edges = N;
	long long Connect = 0;

	// ステップ 4. 頂点個数の数え上げ（実践編）
	BIT Z; Z.init(H + 2);
	for (int i = 0; i <= W; i++) {
		for (int j : V[i][0]) Z.add(G1[j].py, 1);
		for (int j : V[i][1]) {
			Vertex += 1LL * (Z.sum(G2[j].py) - Z.sum(G1[j].py - 1));
		}
		for (int j : V[i][2]) Z.add(G1[j].py, -1);
	}

	// ステップ 5. 連結成分数の数え上げ
	int cnts = 0; S.init(H + 2);
	for (int i = 0; i <= W; i++) {
		for (int j : V[i][0]) {
			cnts++;
			S.update(G1[j].py, cnts);
			Set.insert(G1[j].py);
		}
		for (int j : V[i][1]) {
			auto itr = Set.lower_bound(G1[j].py);
			if (itr == Set.end() || (*itr) > G2[j].py) Connect++;
			else {
				S.conquer(G1[j].py, G2[j].py + 1);
			}
		}
		for (int j : V[i][2]) {
			int val = S.reads(G1[j].py);
			S.update(G1[j].py, 0);
			if (S.check(val) == false) Connect++;
			Set.erase(G1[j].py);
		}
	}

	// ステップ 6. 出力
	cout << Vertex - Edges + Connect << endl;
	return 0;
}
accuracy: 1
time_ms: 640
memory_kb: 146492
final_rank: 11

code_snippet: bool debug=false;
#include <queue>
#include <algorithm>
#include <vector>
#include <utility>
#include <stdio.h>
using namespace std;
#define F first
#define S second
#define PB push_back
const int N=1e5+10;
struct seg_tree{
	vector<int> v,p,cnt,have;
	void init(int n,int l,int r){
		if(v.size()<=n){
			v.resize(n+1);
			cnt.resize(n+1);
		}
		if(l<r){
			int mid=(l+r)>>1;
			init(n*2+1,l,mid);
			init(n*2+2,mid+1,r);
		}
		return ;
	}
	void init(int n){
		p.resize(n);
		for(int i=0;i<n;i++)p[i]=i;
		have.resize(n);
		init(0,0,n-1);
		return ;
	}
	int find(int n){return p[n]==n?n:p[n]=find(p[n]);}
	void push_up(int n){
		cnt[n]=cnt[n*2+1]+cnt[n*2+2];
		if(v[n*2+1]==-1||v[n*2+2]==-1)v[n]=-1;
		else if(find(v[n*2+1])==find(v[n*2+2]))v[n]=find(v[n*2+1]);
		else if(v[n*2+1]==0)v[n]=find(v[n*2+2]);
		else if(v[n*2+2]==0)v[n]=find(v[n*2+1]);
		else v[n]=-1;
	}
	void fix(int n,int l,int r,int pos,int x){
		if(l==r){
			if(x==-1){
				have[pos]--;
				if(have[pos]==0)cnt[n]=v[n]=0;
			}
			else {
				if(have[pos]==0)cnt[n]++;
				v[n]=x;
				have[pos]++;
			}
		}
		else{
			int mid=(l+r)>>1;
			if(pos>mid)fix(n*2+2,mid+1,r,pos,x);
			else fix(n*2+1,l,mid,pos,x);
			push_up(n);
		}
		return ;
	}
	void merge(int n){
		//if(debug)printf("merge(%d)\n",n);
		if(v[n*2+1]==-1)merge(n*2+1);
		if(v[n*2+2]==-1)merge(n*2+2);
		if(v[n*2+1]==0)v[n]=find(v[n*2+2]);
		else if(v[n*2+2]==0)v[n]=find(v[n*2+1]);
		else v[n]=p[find(v[n*2+1])]=find(v[n*2+2]);
		return ;
	}
	pair<int,int> ask(int n,int l,int r,int L,int R){
		//if(debug)printf("ask(%d,%d,%d,%d,%d)\n",n,l,r,L,R);
		if(L<=l&&r<=R){
			if(v[n]==-1)merge(n);
			return {cnt[n],v[n]};
		}
		else if(l>R||L>r)return {0,0};
		else{
			int mid=(l+r)>>1;
			pair<int,int> left=ask(n*2+1,l,mid,L,R),right=ask(n*2+2,mid+1,r,L,R),ans;
			ans.F=left.F+right.F;
			if(left.S==0)ans.S=right.S;
			else if(right.S==0)ans.S=left.S;
			else ans.S=p[find(left.S)]=find(right.S);
			return ans;
		}
	}
	int ask(int L,int R){
		return ask(0,0,(int)p.size()-1,L,R).F;
	}
};
bool cmp(pair<pair<int,int>,int> a,pair<pair<int,int>,int> b){
	return a.S<b.S;
}
int main(){
	int temp,w,h,n,x1,now=1,x2,y1,y2;
	long long int line=0,point=0,comp=0;
	vector<int> x,y;
	vector<pair<pair<int,int>,int>> hor,ver;
	priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> in,out;
	seg_tree seg;
	scanf("%d%d%d",&w,&h,&n);
	for(int i=0;i<n;i++){
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		x.PB(x1);x.PB(x2);
		y.PB(y1);y.PB(y2);
		if(x1==x2)hor.PB({{y1,y2},x1});
		else ver.PB({{x1,x2},y1});
	}
	hor.PB({{0,h},0});
	hor.PB({{0,h},w});
	ver.PB({{0,w},0});
	ver.PB({{0,w},h});
	x.PB(0);
	x.PB(w);
	y.PB(0);
	y.PB(h);
	sort(x.begin(),x.end());
	sort(y.begin(),y.end());
	x.resize(unique(x.begin(),x.end())-x.begin());
	y.resize(unique(y.begin(),y.end())-y.begin());
	for(int i=0;i<hor.size();i++)hor[i]={{lower_bound(y.begin(),y.end(),hor[i].F.F)-y.begin(),lower_bound(y.begin(),y.end(),hor[i].F.S)-y.begin()},lower_bound(x.begin(),x.end(),hor[i].S)-x.begin()};
	for(int i=0;i<ver.size();i++)ver[i]={{lower_bound(x.begin(),x.end(),ver[i].F.F)-x.begin(),lower_bound(x.begin(),x.end(),ver[i].F.S)-x.begin()},lower_bound(y.begin(),y.end(),ver[i].S)-y.begin()};
	for(int i=0;i<hor.size();i++){
		in.push({hor[i].F.F,hor[i].S});
		out.push({hor[i].F.S,hor[i].S});
		point+=2;
		line++;
	}
	sort(ver.begin(),ver.end(),cmp);
	n+=10;
	n<<=1;
	seg.init(n);
	for(int i=0;i<ver.size();i++){
		if(debug)printf("ver[%d]=(%d,%d,%d)\n",i,ver[i].F.F,ver[i].F.S,ver[i].S);
		while(!in.empty()){
			if(in.top().F<=ver[i].S){
				seg.fix(0,0,n-1,in.top().S,now++);
				in.pop();
			}
			else break;
		}
		while(!out.empty()){
			if(out.top().F<ver[i].S){
				seg.fix(0,0,n-1,out.top().S,-1);
				out.pop();
			}
			else break;
		}
		temp=seg.ask(ver[i].F.F,ver[i].F.S);
		point+=temp+2;
		line+=(temp<<1)+1;
		if(temp==0)comp++;
		if(debug)printf("%d::temp=%d point=%d line=%d comp=%d\n",i,temp,point,line,comp);
		if(debug)printf("now=%d\n",now);
		if(0&&debug){
			printf("p::");
			for(int i=1;i<now;i++)printf(" %d",seg.p[i]);
			printf("\n");
		}
	}
	while(!in.empty()){
		in.pop();
		comp++;
	}
	for(int i=1;i<now;i++)if(seg.p[i]==i)comp++;
	printf("%lld\n",line-point+comp);
	if(debug)printf("line=%d point=%d comp=%d\n",line,point,comp);
}
accuracy: 1
time_ms: 140
memory_kb: 14188
final_rank: 2

code_snippet: bool debug=false;
#include <queue>
#include <algorithm>
#include <vector>
#include <utility>
#include <stdio.h>
using namespace std;
#define F first
#define S second
#define PB push_back
const int N=1e5+10;
struct seg_tree{
	vector<int> v,p,cnt,have;
	void init(int n,int l,int r){
		if(v.size()<=n){
			v.resize(n+1);
			cnt.resize(n+1);
		}
		if(l<r){
			int mid=(l+r)>>1;
			init(n*2+1,l,mid);
			init(n*2+2,mid+1,r);
		}
		return ;
	}
	void init(int n){
		p.resize(n);
		for(int i=0;i<n;i++)p[i]=i;
		have.resize(n);
		init(0,0,n-1);
		return ;
	}
	int find(int n){return p[n]==n?n:p[n]=find(p[n]);}
	void push_up(int n){
		cnt[n]=cnt[n*2+1]+cnt[n*2+2];
		if(v[n*2+1]==-1||v[n*2+2]==-1)v[n]=-1;
		else if(find(v[n*2+1])==find(v[n*2+2]))v[n]=find(v[n*2+1]);
		else if(v[n*2+1]==0)v[n]=find(v[n*2+2]);
		else if(v[n*2+2]==0)v[n]=find(v[n*2+1]);
		else v[n]=-1;
	}
	void fix(int n,int l,int r,int pos,int x){
		if(l==r){
			if(x==-1){
				have[pos]--;
				if(have[pos]==0)cnt[n]=v[n]=0;
			}
			else {
				if(have[pos]==0)cnt[n]++;
				v[n]=x;
				have[pos]++;
			}
		}
		else{
			int mid=(l+r)>>1;
			if(pos>mid)fix(n*2+2,mid+1,r,pos,x);
			else fix(n*2+1,l,mid,pos,x);
			push_up(n);
		}
		return ;
	}
	void merge(int n){
		//if(debug)printf("merge(%d)\n",n);
		if(v[n*2+1]==-1)merge(n*2+1);
		if(v[n*2+2]==-1)merge(n*2+2);
		if(v[n*2+1]==0)v[n]=find(v[n*2+2]);
		else if(v[n*2+2]==0)v[n]=find(v[n*2+1]);
		else v[n]=p[find(v[n*2+1])]=find(v[n*2+2]);
		return ;
	}
	pair<int,int> ask(int n,int l,int r,int L,int R){
		//if(debug)printf("ask(%d,%d,%d,%d,%d)\n",n,l,r,L,R);
		if(L<=l&&r<=R){
			if(v[n]==-1)merge(n);
			return {cnt[n],v[n]};
		}
		else if(l>R||L>r)return {0,0};
		else{
			int mid=(l+r)>>1;
			pair<int,int> left=ask(n*2+1,l,mid,L,R),right=ask(n*2+2,mid+1,r,L,R),ans;
			ans.F=left.F+right.F;
			if(left.S==0)ans.S=right.S;
			else if(right.S==0)ans.S=left.S;
			else ans.S=p[find(left.S)]=find(right.S);
			return ans;
		}
	}
	int ask(int L,int R){
		return ask(0,0,(int)p.size()-1,L,R).F;
	}
};
bool cmp(pair<pair<int,int>,int> a,pair<pair<int,int>,int> b){
	return a.S<b.S;
}
int main(){
	int temp,line=0,point=0,comp=0,w,h,n,x1,now=1,x2,y1,y2;
	vector<int> x,y;
	vector<pair<pair<int,int>,int>> hor,ver;
	priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>> in,out;
	seg_tree seg;
	scanf("%d%d%d",&w,&h,&n);
	for(int i=0;i<n;i++){
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		x.PB(x1);x.PB(x2);
		y.PB(y1);y.PB(y2);
		if(x1==x2)hor.PB({{y1,y2},x1});
		else ver.PB({{x1,x2},y1});
	}
	hor.PB({{0,h},0});
	hor.PB({{0,h},w});
	ver.PB({{0,w},0});
	ver.PB({{0,w},h});
	x.PB(0);
	x.PB(w);
	y.PB(0);
	y.PB(h);
	sort(x.begin(),x.end());
	sort(y.begin(),y.end());
	x.resize(unique(x.begin(),x.end())-x.begin());
	y.resize(unique(y.begin(),y.end())-y.begin());
	for(int i=0;i<hor.size();i++)hor[i]={{lower_bound(y.begin(),y.end(),hor[i].F.F)-y.begin(),lower_bound(y.begin(),y.end(),hor[i].F.S)-y.begin()},lower_bound(x.begin(),x.end(),hor[i].S)-x.begin()};
	for(int i=0;i<ver.size();i++)ver[i]={{lower_bound(x.begin(),x.end(),ver[i].F.F)-x.begin(),lower_bound(x.begin(),x.end(),ver[i].F.S)-x.begin()},lower_bound(y.begin(),y.end(),ver[i].S)-y.begin()};
	for(int i=0;i<hor.size();i++){
		in.push({hor[i].F.F,hor[i].S});
		out.push({hor[i].F.S,hor[i].S});
		point+=2;
		line++;
	}
	sort(ver.begin(),ver.end(),cmp);
	n+=10;
	n<<=1;
	seg.init(n);
	for(int i=0;i<ver.size();i++){
		if(debug)printf("ver[%d]=(%d,%d,%d)\n",i,ver[i].F.F,ver[i].F.S,ver[i].S);
		while(!in.empty()){
			if(in.top().F<=ver[i].S){
				seg.fix(0,0,n-1,in.top().S,now++);
				in.pop();
			}
			else break;
		}
		while(!out.empty()){
			if(out.top().F<ver[i].S){
				seg.fix(0,0,n-1,out.top().S,-1);
				out.pop();
			}
			else break;
		}
		temp=seg.ask(ver[i].F.F,ver[i].F.S);
		point+=temp+2;
		line+=(temp<<1)+1;
		if(temp==0)comp++;
		if(debug)printf("%d::temp=%d point=%d line=%d comp=%d\n",i,temp,point,line,comp);
		if(debug)printf("now=%d\n",now);
		if(0&&debug){
			printf("p::");
			for(int i=1;i<now;i++)printf(" %d",seg.p[i]);
			printf("\n");
		}
	}
	while(!in.empty()){
		in.pop();
		comp++;
	}
	for(int i=1;i<now;i++)if(seg.p[i]==i)comp++;
	printf("%d\n",line-point+comp);
	if(debug)printf("line=%d point=%d comp=%d\n",line,point,comp);
}
accuracy: 0.6351351351351351
time_ms: 140
memory_kb: 13540
final_rank: 12

code_snippet: #include <cstdio>
#include <cstdlib>
#include <cmath>
#include <climits>
#include <cfloat>
#include <cstring>
#include <map>
#include <utility>
#include <set>
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <list>
#include <algorithm>
#include <functional>
#include <sstream>
#include <complex>
#include <stack>
#include <queue>
#include <unordered_set>
#include <unordered_map>
#include <array>
#include <cassert>
#include <bitset>
using namespace std;
using LL = long long;

class union_find {
private:
	vector<int>parent;
	vector<int>rank;
	vector<int>clump;
public:
	union_find(int siz) {
		parent.resize(siz);
		rank.resize(siz);
		clump.resize(siz);
		for (int i = 0; i < siz; ++i) {
			parent[i] = i;
			rank[i] = 0;
			clump[i] = 1;
		}
	}
	void unite(int x, int y) {
		int xr = find(x);
		int yr = find(y);
		if (xr == yr)return;
		if (rank[xr] > rank[yr]) {
			parent[yr] = xr;
			clump[xr] += clump[yr];
		}
		else if (rank[yr] > rank[xr]) {
			parent[xr] = yr;
			clump[yr] += clump[xr];
		}
		else if (xr != yr) {
			parent[yr] = xr;
			clump[xr] += clump[yr];
			++rank[xr];
		}
	}
	int find(int x) {
		if (parent[x] == x) {
			return x;
		}
		else {
			return parent[x] = find(parent[x]);
		}
	}
	bool check(int x, int y) {
		return find(x) == find(y);
	}
	int size(int x) {
		return clump[find(x)];
	}
};

struct line {
	bool dir;//false:横 true:縦
	LL pos;
	LL b1, b2;
	int id;
	line(bool dir, LL pos, LL b1, LL b2, int id)
		:dir(dir), pos(pos), b1(b1), b2(b2), id(id)
	{}
	bool operator<(const line& o) const {
		if (pos == o.pos) {
			if (b1 == o.b1) {
				return b2 < o.b2;
			}
			return b1 < o.b1;
		}
		return pos < o.pos;
	}
};
vector<line>vec;
vector<LL>tate;
LL W, H, N;

template<typename T>
class BIT {
private:
	vector<T>arr;
	int siz;
public:
	BIT(int n) :siz(1) {
		while (siz < n)siz *= 2;
		arr.resize(siz + 1);
	}
	//[0,x)までの和を取得する
	T query(int x) const {
		T s = 0;
		while (x > 0) {
			s += arr[x];
			x -= (x & -x);
		}
		return s;
	}
	//xに値kを加算する
	void add(int x, const T& k) {
		++x;
		while (x <= siz) {
			arr[x] += k;
			x += (x & -x);
		}
	}
};


vector<LL>border;
vector<vector<pair<pair<LL, LL>, int>>>seg;
int segwidth = 1;

void query(int a, int b, union_find& uf, int id, int yok, int k = 0, int l = 0, int r = -1) {
	if (r < 0)r = segwidth;
	if (r <= a || b <= l)return;
	if (a <= l && r <= b) {
		//マージする
		if (seg[k].empty())return;
		int lim = -1;
		int endid = 0;
		for (int xx = seg[k].size() - 1; xx >= 0; --xx) {
			if (seg[k][xx].first.first >= yok) {
				lim = xx;
				break;
			}
			if (seg[k][xx].first.first < yok && yok < seg[k][xx].first.second) {
				uf.unite(id, seg[k][xx].second);
				endid = seg[k][xx].second;
			}
		}
		bool flag = false;
		auto very = seg[k][seg[k].size() - 1];
		very.second = endid;
		for (int xx = seg[k].size() - 1; xx > lim; --xx) {
			flag = true;
			very.first.first = min(very.first.first, seg[k][xx].first.first);
			very.first.second = max(very.first.second, seg[k][xx].first.second);
			seg[k].pop_back();
		}
		if (flag) {
			seg[k].push_back(very);
		}
		return;
	}
	int mid = (l + r) / 2;
	query(a, b, uf, id, yok, k * 2 + 1, l, mid);
	query(a, b, uf, id, yok, k * 2 + 2, mid, r);
}

int main(void)
{
	cin >> W >> H >> N;
	for (int i = 0; i < N; ++i) {
		LL a, b, c, d;
		cin >> a >> b >> c >> d;
		if (a == c) {
			//縦
			LL up = min(b, d);
			LL dn = max(b, d);
			vec.push_back({ true,a * 2,up * 3 - 1,dn * 3 + 1 ,i });
			tate.push_back(up * 3 - 1);
			tate.push_back(dn * 3 + 1);
			border.push_back(up * 3 - 1);
			border.push_back(dn * 3 + 1);
		}
		else {
			//横
			LL lf = min(a, c);
			LL rh = max(a, c);
			vec.push_back({ false, b * 3,2 * lf - 1,2 * rh + 1,i });
			tate.push_back(b * 3);
		}
	}
	vec.push_back({ true,0,-1,H * 3 + 1 ,(int)N });
	vec.push_back({ true,W * 2,-1,H * 3 + 1,(int)N + 1 });
	vec.push_back({ false,0,-1,W * 2 + 1 ,(int)N + 2 });
	vec.push_back({ false,H * 3,-1,W * 2 + 1 ,(int)N + 3 });
	tate.push_back(0);
	tate.push_back(H * 3);
	tate.push_back(-1);
	tate.push_back(H * 3 + 1);
	border.push_back(-1);
	border.push_back(H * 3 + 1);
	sort(tate.begin(), tate.end());
	tate.erase(unique(tate.begin(), tate.end()), tate.end());
	sort(border.begin(), border.end());
	border.erase(unique(border.begin(), border.end()), border.end());
	N += 4;
	LL M = tate.size();
	map<LL, int>inv;
	for (int i = 0; i < M; ++i) {
		inv[tate[i]] = i;
	}
	vector<pair<LL, pair<LL, LL>>>events;
	LL crossnum = 0;
	for (auto& elm : vec) {
		if (elm.dir) {
			events.push_back({ elm.pos,{ elm.b1,elm.b2 } });
		}
		else {
			events.push_back({ elm.b1,{ elm.pos,elm.pos } });
			events.push_back({ elm.b2,{ elm.pos,elm.pos } });
		}
	}
	BIT<int>bit(M);
	sort(events.begin(), events.end());
	for (auto& elm : events) {
		if (elm.second.first == elm.second.second) {
			int index = inv[elm.second.first];
			if (bit.query(index + 1) - bit.query(index)) {
				bit.add(index, -1);
			}
			else {
				bit.add(index, 1);
			}
		}
		else {
			int rind = inv[elm.second.second];
			int lind = inv[elm.second.first];
			crossnum += (bit.query(rind) - bit.query(lind));
		}
	}
	assert(border.size());
	int rawwidth = border.size() - 1;
	while (segwidth < rawwidth) {
		segwidth *= 2;
	}
	seg.resize(segwidth * 2 - 1);
	//横の線分をセグ木に入れていく
	for (auto& elm : vec) {
		if (!elm.dir) {
			LL y = elm.pos;
			int index = lower_bound(border.begin(), border.end(), y) - border.begin();
			if (index != 0 && index != border.size()) {
				//index-1番目に格納する
				seg[index - 1 + segwidth - 1].push_back({ { elm.b1,elm.b2 } ,elm.id });
			}
		}
	}
	//セグ木情報を上に伝播させていく
	for (int k = segwidth - 2; k >= 0; --k) {
		for (auto& par : seg[k * 2 + 1]) {
			seg[k].push_back(par);
		}
		for (auto& par : seg[k * 2 + 2]) {
			seg[k].push_back(par);
		}
	}
	//セグ木内部の線分を区分ごとにソートする(stack的に後ろから見る)
	for (int k = 0; k < segwidth * 2 - 1; ++k) {
		sort(seg[k].rbegin(), seg[k].rend());
	}
	//縦直線をクエリで処理
	union_find uf(N);
	sort(vec.begin(), vec.end());
	for (auto&elm : vec) {
		if (elm.dir) {
			int lef = lower_bound(border.begin(), border.end(), elm.b1) - border.begin();
			int rht = lower_bound(border.begin(), border.end(), elm.b2) - border.begin();
			query(lef, rht, uf, elm.id, elm.pos);
		}
	}
	//親の種類数で断定
	set<int>parset;
	for (int i = 0; i < N; ++i) {
		parset.insert(uf.find(i));
	}
	LL groupnum = parset.size();
	LL answer = crossnum + groupnum - N;
	cout << answer << endl;
	return 0;
}
accuracy: 1
time_ms: 390
memory_kb: 61280
final_rank: 7

code_snippet: #include<bits/stdc++.h>
const long long N = 2e5 + 5;
using namespace std;

typedef pair <long long, long long> ii;
typedef pair <ii, long long> iii;
vector <ii> vc;
vector <iii> mv;

long long w, h, n, x[2][N], y[2][N], a[20*N], pos[2][N], num, bit[N];
long long cnt, ver, root_ver[N], ans;
bool check[20*N];

struct {
    long long pset[N], numdsu;

    void init (long long n){
        for (long long i = 1; i <= n; i++) pset[i] = i;
        numdsu = n;
    }

    long long findset(long long i) {
        if (pset[i] != i) pset[i] = findset(pset[i]);
        return pset[i];
    }

    void unionset(long long i, long long j){
        if (findset(i) == findset(j)) return;
        pset[findset(i)] = findset(j);
        numdsu--;
    }
} dsu;

struct node {
    long long val, left, right, cnt;
} lv[20*N];

void init(long long l, long long r){
    long long nod = cnt;
    if (l != r){
        long long mid = (l + r) / 2;
        lv[nod].left  = ++cnt; init (l, mid);
        lv[nod].right = ++cnt; init (mid+1, r);
    }
}

void dolazy(long long k, long long l, long long r){
    if (lv[k].val == 0) return;
    if (l != r) {
        long long a = lv[k].left, b = lv[k].right;
        if (lv[a].val) {dsu.unionset(lv[a].val, lv[k].val);}
        if (lv[b].val) {dsu.unionset(lv[b].val, lv[k].val);}
        if (lv[a].cnt) lv[a].val = lv[k].val;
        if (lv[b].cnt) lv[b].val = lv[k].val;
        lv[k].val = 0;
    }
}

void update(long long k, long long l, long long r, long long pos, long long val){
    dolazy(k, l, r);
    if (l > pos || pos > r) return;
    if (l == r){
        lv[cnt].val = val;
        lv[cnt].cnt = (val != 0);
        return;
    }
    long long mid = (l + r) / 2; long long nod = cnt;
    if (pos > mid){
        lv[nod].left = lv[k].left;
        lv[nod].right = ++cnt;
        update(lv[k].right, mid+1, r, pos, val);
    }
    else{
        lv[nod].left = ++cnt;
        lv[nod].right = lv[k].right;
        update(lv[k].left, l, mid, pos, val);
    }
    lv[nod].cnt = lv[lv[nod].left].cnt + lv[lv[nod].right].cnt;
}

void update(long long pos, long long val){
    root_ver[++ver] = ++cnt;
    update(root_ver[ver-1], 1, num, pos, val);
}

void update (long long k, long long l, long long r, long long L, long long R, long long val){
    dolazy(k, l, r);
    if (l > R || L > r) return;
    if (L <= l && r <= R){
        if (lv[k].val) dsu.unionset(lv[k].val, val);
        if (lv[k].cnt) lv[k].val = val;
        return;
    }
    long long mid = (l + r) / 2;
    update(lv[k].left, l, mid, L, R, val); update(lv[k].right, mid+1, r, L, R, val);
}

void dfs(long long u){
    if (check[u]) return;
    check[u] = 1; a[u] = lv[u].val; long long v;
    v = lv[u].left;  dfs(v);
    if (a[u] && a[v]) {dsu.unionset(a[u], a[v]);}
    else a[u] = max(a[u], a[v]);
    v = lv[u].right; dfs(v);
    if (a[u] && a[v]) {dsu.unionset(a[u], a[v]);}
    else a[u] = max(a[u], a[v]);
}

void updatebit(long long i, long long val){
    while (i <= num) bit[i]+=val, i += i&(-i);
}

long long get(long long l, long long r){
    long long ans = 0; l--;
    while (r > 0) ans += bit[r], r -= r&(-r);
    while (l > 0) ans -= bit[l], l -= l&(-l);
    return ans;
}

void init(){
    cin >> w >> h >> n;
    for (long long i = 1; i <= n; i++) cin >> x[0][i] >> y[0][i] >> x[1][i] >> y[1][i];
    x[0][n+1] = 0; y[0][n+1] = 0; x[1][n+1] = w; y[1][n+1] = 0;
    x[0][n+2] = 0; y[0][n+2] = h; x[1][n+2] = w; y[1][n+2] = h;
    x[0][n+3] = 0; y[0][n+3] = 0; x[1][n+3] = 0; y[1][n+3] = h;
    x[0][n+4] = w; y[0][n+4] = 0; x[1][n+4] = w; y[1][n+4] = h;
    n += 4;
    for (long long i = 1; i <= n; i++){
        if (x[0][i] > x[1][i] || y[0][i] > y[1][i]){
            swap(x[0][i], x[1][i]);
            swap(y[0][i], y[1][i]);
        }
        if (x[0][i] != x[1][i]) {
            mv.push_back(iii(ii(x[0][i], -1), i));
            mv.push_back(iii(ii(x[1][i],  1), i));
            vc.push_back(ii(y[0][i], i));
        }
        else{
            mv.push_back(iii(ii(x[0][i],  0), i));
            vc.push_back(ii(y[0][i], i));
            vc.push_back(ii(y[1][i], i));
        }
    }
    sort (mv.begin(), mv.end());
    sort (vc.begin(), vc.end());
    dsu.init(n);
    ans = -n;
    for (long long i = 0; i < vc.size(); i++){
        if (i == 0 || (i > 0 && vc[i].first != vc[i-1].first)) num++;
        long long z = vc[i].second;
        if (pos[0][z]) pos[1][z] = num;
        else pos[0][z] = num;
    }
    root_ver[++ver] = ++cnt; init(1, num);
}

int main(){
    ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    init();
    for (long long i = 0; i < mv.size(); i++){
        long long z = mv[i].second, val = mv[i].first.first;

        if (val == x[0][z] && val == x[1][z]) update(root_ver[ver], 1, num, pos[0][z], pos[1][z], z);
        else if (val == x[0][z]) update(pos[0][z], z);
        else update(pos[0][z], 0);

        if (val == x[0][z] && val == x[1][z]) ans += get(pos[0][z], pos[1][z]);
        else if (val == x[0][z]) updatebit(pos[0][z], 1);
        else updatebit(pos[0][z], -1);
    }
    for (long long i = 1; i <= cnt; i++) if (lv[i].val) dfs(i);
    ans += dsu.numdsu;
    cout << ans << "\n";
}
accuracy: 1
time_ms: 240
memory_kb: 149448
final_rank: 9

code_snippet: #include<bits/stdc++.h>
const long long N = 3e5 + 5;
using namespace std;

typedef pair <long long, long long> ii;
typedef pair <ii, long long> iii;
vector <ii> vc;
vector <iii> mv;

long long w, h, n, x[2][N], y[2][N], a[N], pos[2][N], num, bit[N];
long long cnt, ver, root_ver[N], ans;
bool check[N];

struct {
    long long pset[N], numdsu;

    void init (long long n){
        for (long long i = 1; i <= n; i++) pset[i] = i;
        numdsu = n;
    }

    long long findset(long long i) {
        if (pset[i] != i) pset[i] = findset(pset[i]);
        return pset[i];
    }

    void unionset(long long i, long long j){
        if (findset(i) == findset(j)) return;
        pset[findset(i)] = findset(j);
        numdsu--;
    }
} dsu;

struct node {
    long long val, left, right, cnt;
} lv[30*N];

void init(long long l, long long r){
    long long nod = cnt;
    if (l != r){
        long long mid = (l + r) / 2;
        lv[nod].left  = ++cnt; init (l, mid);
        lv[nod].right = ++cnt; init (mid+1, r);
    }
}

void dolazy(long long k, long long l, long long r){
    if (lv[k].val == 0) return;
    if (l != r) {
        long long a = lv[k].left, b = lv[k].right;
        if (lv[a].val) {dsu.unionset(lv[a].val, lv[k].val);}
        if (lv[b].val) {dsu.unionset(lv[b].val, lv[k].val);}
        if (lv[a].cnt) lv[a].val = lv[k].val;
        if (lv[b].cnt) lv[b].val = lv[k].val;
        lv[k].val = 0;
    }
}

void update(long long k, long long l, long long r, long long pos, long long val){
    dolazy(k, l, r);
    if (l > pos || pos > r) return;
    if (l == r){
        lv[cnt].val = val;
        lv[cnt].cnt = (val != 0);
        return;
    }
    long long mid = (l + r) / 2; long long nod = cnt;
    if (pos > mid){
        lv[nod].left = lv[k].left;
        lv[nod].right = ++cnt;
        update(lv[k].right, mid+1, r, pos, val);
    }
    else{
        lv[nod].left = ++cnt;
        lv[nod].right = lv[k].right;
        update(lv[k].left, l, mid, pos, val);
    }
    lv[nod].cnt = lv[lv[nod].left].cnt + lv[lv[nod].right].cnt;
}

void update(long long pos, long long val){
    root_ver[++ver] = ++cnt;
    update(root_ver[ver-1], 1, num, pos, val);
}

void update (long long k, long long l, long long r, long long L, long long R, long long val){
    dolazy(k, l, r);
    if (l > R || L > r) return;
    if (L <= l && r <= R){
        if (lv[k].val) dsu.unionset(lv[k].val, val);
        if (lv[k].cnt) lv[k].val = val;
        return;
    }
    long long mid = (l + r) / 2;
    update(lv[k].left, l, mid, L, R, val); update(lv[k].right, mid+1, r, L, R, val);
}

void dfs(long long u){
    if (check[u]) return;
    check[u] = 1; a[u] = lv[u].val; long long v;
    v = lv[u].left;  dfs(v);
    if (a[u] && a[v]) {dsu.unionset(a[u], a[v]);}
    else a[u] = max(a[u], a[v]);
    v = lv[u].right; dfs(v);
    if (a[u] && a[v]) {dsu.unionset(a[u], a[v]);}
    else a[u] = max(a[u], a[v]);
}

void updatebit(long long i, long long val){
    while (i <= num) bit[i]+=val, i += i&(-i);
}

long long get(long long l, long long r){
    long long ans = 0; l--;
    while (r > 0) ans += bit[r], r -= r&(-r);
    while (l > 0) ans -= bit[l], l -= l&(-l);
    return ans;
}

void init(){
    cin >> w >> h >> n;
    for (long long i = 1; i <= n; i++) cin >> x[0][i] >> y[0][i] >> x[1][i] >> y[1][i];
    x[0][n+1] = 0; y[0][n+1] = 0; x[1][n+1] = w; y[1][n+1] = 0;
    x[0][n+2] = 0; y[0][n+2] = h; x[1][n+2] = w; y[1][n+2] = h;
    x[0][n+3] = 0; y[0][n+3] = 0; x[1][n+3] = 0; y[1][n+3] = h;
    x[0][n+4] = w; y[0][n+4] = 0; x[1][n+4] = w; y[1][n+4] = h;
    n += 4;
    for (long long i = 1; i <= n; i++){
        if (x[0][i] > x[1][i] || y[0][i] > y[1][i]){
            swap(x[0][i], x[1][i]);
            swap(y[0][i], y[1][i]);
        }
        if (x[0][i] != x[1][i]) {
            mv.push_back(iii(ii(x[0][i], -1), i));
            mv.push_back(iii(ii(x[1][i],  1), i));
            vc.push_back(ii(y[0][i], i));
        }
        else{
            mv.push_back(iii(ii(x[0][i],  0), i));
            vc.push_back(ii(y[0][i], i));
            vc.push_back(ii(y[1][i], i));
        }
    }
    sort (mv.begin(), mv.end());
    sort (vc.begin(), vc.end());
    dsu.init(n);
    ans = -n;
    for (long long i = 0; i < vc.size(); i++){
        if (i == 0 || (i > 0 && vc[i].first != vc[i-1].first)) num++;
        long long z = vc[i].second;
        if (pos[0][z]) pos[1][z] = num;
        else pos[0][z] = num;
    }
    root_ver[++ver] = ++cnt; init(1, num);
}

int main(){
    ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    init();
    for (long long i = 0; i < mv.size(); i++){
        long long z = mv[i].second, val = mv[i].first.first;
        if (val == x[0][z] && val == x[1][z]) update(root_ver[ver], 1, num, pos[0][z], pos[1][z], z);
        else if (val == x[0][z]) update(pos[0][z], z);
        else update(pos[0][z], 0);
        if (val == x[0][z] && val == x[1][z]) ans += get(pos[0][z], pos[1][z]);
        else if (val == x[0][z]) updatebit(pos[0][z], 1);
        else updatebit(pos[0][z], -1);
    }
    for (long long i = 1; i <= cnt; i++) if (lv[i].val) dfs(i);
    ans += dsu.numdsu;
    cout << ans << "\n";
}
/*
10 10 1
2 3 2 4
*/
accuracy: 0.4864864864864865
time_ms: 270
memory_kb: 91476
final_rank: 15

code_snippet: #include<bits/stdc++.h>
const int N = 2e5 + 50;
using namespace std;

typedef pair <int, int> ii;
typedef pair <ii, int> iii;
vector <ii> vc;
vector <iii> mv;

int w, h, n, x[2][N], y[2][N], a[N], pos[2][N], num, bit[N];
int cnt, ver, root_ver[N], ans;
bool check[N];

struct {
    int pset[N], numdsu;

    void init (int n){
        for (int i = 1; i <= n; i++) pset[i] = i;
        numdsu = n;
    }

    int findset(int i) {
        if (pset[i] != i) pset[i] = findset(pset[i]);
        return pset[i];
    }

    void unionset(int i, int j){
        if (findset(i) == findset(j)) return;
        pset[findset(i)] = findset(j);
        numdsu--;
    }
} dsu;

struct node {
    int val, left, right, cnt;
} lv[30*N];

void init(int l, int r){
    int nod = cnt;
    if (l != r){
        int mid = (l + r) / 2;
        lv[nod].left  = ++cnt; init (l, mid);
        lv[nod].right = ++cnt; init (mid+1, r);
    }
}

void dolazy(int k, int l, int r){
    if (lv[k].val == 0) return;
    if (l != r) {
        int a = lv[k].left, b = lv[k].right;
        if (lv[a].val) {dsu.unionset(lv[a].val, lv[k].val);}
        if (lv[b].val) {dsu.unionset(lv[b].val, lv[k].val);}
        if (lv[a].cnt) lv[a].val = lv[k].val;
        if (lv[b].cnt) lv[b].val = lv[k].val;
        lv[k].val = 0;
    }
}

void update(int k, int l, int r, int pos, int val){
    dolazy(k, l, r);
    if (l > pos || pos > r) return;
    if (l == r){
        lv[cnt].val = val;
        lv[cnt].cnt = (val != 0);
        return;
    }
    int mid = (l + r) / 2; int nod = cnt;
    if (pos > mid){
        lv[nod].left = lv[k].left;
        lv[nod].right = ++cnt;
        update(lv[k].right, mid+1, r, pos, val);
    }
    else{
        lv[nod].left = ++cnt;
        lv[nod].right = lv[k].right;
        update(lv[k].left, l, mid, pos, val);
    }
    lv[nod].cnt = lv[lv[nod].left].cnt + lv[lv[nod].right].cnt;
}

void update(int pos, int val){
    root_ver[++ver] = ++cnt;
    update(root_ver[ver-1], 1, num, pos, val);
}

void update (int k, int l, int r, int L, int R, int val){
    dolazy(k, l, r);
    if (l > R || L > r) return;
    if (L <= l && r <= R){
        if (lv[k].val) dsu.unionset(lv[k].val, val);
        if (lv[k].cnt) lv[k].val = val;
        return;
    }
    int mid = (l + r) / 2;
    update(lv[k].left, l, mid, L, R, val); update(lv[k].right, mid+1, r, L, R, val);
}

void dfs(int u){
    if (check[u]) return;
    check[u] = 1; a[u] = lv[u].val; int v;
    v = lv[u].left;  dfs(v);
    if (a[u] && a[v]) {dsu.unionset(a[u], a[v]);}
    else a[u] = max(a[u], a[v]);
    v = lv[u].right; dfs(v);
    if (a[u] && a[v]) {dsu.unionset(a[u], a[v]);}
    else a[u] = max(a[u], a[v]);
}

void updatebit(int i, int val){
    while (i <= num) bit[i]+=val, i += i&(-i);
}

int get(int l, int r){
    int ans = 0; l--;
    while (r > 0) ans += bit[r], r -= r&(-r);
    while (l > 0) ans -= bit[l], l -= l&(-l);
    return ans;
}

void init(){
    cin >> w >> h >> n;
    for (int i = 1; i <= n; i++) cin >> x[0][i] >> y[0][i] >> x[1][i] >> y[1][i];
    x[0][n+1] = 0; y[0][n+1] = 0; x[1][n+1] = w; y[1][n+1] = 0;
    x[0][n+2] = 0; y[0][n+2] = h; x[1][n+2] = w; y[1][n+2] = h;
    x[0][n+3] = 0; y[0][n+3] = 0; x[1][n+3] = 0; y[1][n+3] = h;
    x[0][n+4] = w; y[0][n+4] = 0; x[1][n+4] = w; y[1][n+4] = h;
    n += 4;
    for (int i = 1; i <= n; i++){
        if (x[0][i] > x[1][i] || y[0][i] > y[1][i]){
            swap(x[0][i], x[1][i]);
            swap(y[0][i], y[1][i]);
        }
        if (x[0][i] != x[1][i]) {
            mv.push_back(iii(ii(x[0][i], -1), i));
            mv.push_back(iii(ii(x[1][i],  1), i));
            vc.push_back(ii(y[0][i], i));
        }
        else{
            mv.push_back(iii(ii(x[0][i],  0), i));
            vc.push_back(ii(y[0][i], i));
            vc.push_back(ii(y[1][i], i));
        }
    }
    sort (mv.begin(), mv.end());
    sort (vc.begin(), vc.end());
    dsu.init(n);
    ans = -n;
    for (int i = 0; i < vc.size(); i++){
        if (i == 0 || (i > 0 && vc[i].first != vc[i-1].first)) num++;
        int z = vc[i].second;
        if (pos[0][z]) pos[1][z] = num;
        else pos[0][z] = num;
    }
    root_ver[++ver] = ++cnt; init(1, num);
}

int main(){
    ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    init();
    for (int i = 0; i < mv.size(); i++){
        int z = mv[i].second, val = mv[i].first.first;
        if (val == x[0][z] && val == x[1][z]) update(root_ver[ver], 1, num, pos[0][z], pos[1][z], z);
        else if (val == x[0][z]) update(pos[0][z], z);
        else update(pos[0][z], 0);
        if (val == x[0][z] && val == x[1][z]) ans += get(pos[0][z], pos[1][z]);
        else if (val == x[0][z]) updatebit(pos[0][z], 1);
        else updatebit(pos[0][z], -1);
    }
    for (int i = 1; i <= cnt; i++) if (lv[i].val) dfs(i);
    ans += dsu.numdsu;
    cout << ans << "\n";
}
/*
10 10 1
2 3 2 4
*/
accuracy: 0.4864864864864865
time_ms: 160
memory_kb: 45528
final_rank: 14

code_snippet: #include<bits/stdc++.h>
const int N = 2e5 + 5;
using namespace std;

typedef pair <int, int> ii;
typedef pair <ii, int> iii;
vector <ii> vc;
vector <iii> mv;

int w, h, n, x[2][N], y[2][N], a[N], pos[2][N], num, bit[N];
int cnt, ver, root_ver[N], ans;
bool check[N];

struct {
    int pset[N], numdsu;

    void init (int n){
        for (int i = 1; i <= n; i++) pset[i] = i;
        numdsu = n;
    }

    int findset(int i) {
        if (pset[i] != i) pset[i] = findset(pset[i]);
        return pset[i];
    }

    void unionset(int i, int j){
        if (findset(i) == findset(j)) return;
        pset[findset(i)] = findset(j);
        numdsu--;
    }
} dsu;

struct node {
    int val, left, right, cnt;
} lv[20*N];

void init(int l, int r){
    int nod = cnt;
    if (l != r){
        int mid = (l + r) / 2;
        lv[nod].left  = ++cnt; init (l, mid);
        lv[nod].right = ++cnt; init (mid+1, r);
    }
}

void dolazy(int k, int l, int r){
    if (lv[k].val == 0) return;
    if (l != r) {
        int a = lv[k].left, b = lv[k].right;
        if (lv[a].val) {dsu.unionset(lv[a].val, lv[k].val);}
        if (lv[b].val) {dsu.unionset(lv[b].val, lv[k].val);}
        if (lv[a].cnt) lv[a].val = lv[k].val;
        if (lv[b].cnt) lv[b].val = lv[k].val;
        lv[k].val = 0;
    }
}

void update(int k, int l, int r, int pos, int val){
    dolazy(k, l, r);
    if (l > pos || pos > r) return;
    if (l == r){
        lv[cnt].val = val;
        lv[cnt].cnt = (val != 0);
        return;
    }
    int mid = (l + r) / 2; int nod = cnt;
    if (pos > mid){
        lv[nod].left = lv[k].left;
        lv[nod].right = ++cnt;
        update(lv[k].right, mid+1, r, pos, val);
    }
    else{
        lv[nod].left = ++cnt;
        lv[nod].right = lv[k].right;
        update(lv[k].left, l, mid, pos, val);
    }
    lv[nod].cnt = lv[lv[nod].left].cnt + lv[lv[nod].right].cnt;
}

void update(int pos, int val){
    root_ver[++ver] = ++cnt;
    update(root_ver[ver-1], 1, num, pos, val);
}

void update (int k, int l, int r, int L, int R, int val){
    dolazy(k, l, r);
    if (l > R || L > r) return;
    if (L <= l && r <= R){
        if (lv[k].val) dsu.unionset(lv[k].val, val);
        if (lv[k].cnt) lv[k].val = val;
        return;
    }
    int mid = (l + r) / 2;
    update(lv[k].left, l, mid, L, R, val); update(lv[k].right, mid+1, r, L, R, val);
}

void dfs(int u){
    if (check[u]) return;
    check[u] = 1; a[u] = lv[u].val; int v;
    v = lv[u].left;  dfs(v);
    if (a[u] && a[v]) {dsu.unionset(a[u], a[v]);}
    else a[u] = max(a[u], a[v]);
    v = lv[u].right; dfs(v);
    if (a[u] && a[v]) {dsu.unionset(a[u], a[v]);}
    else a[u] = max(a[u], a[v]);
}

void updatebit(int i, int val){
    while (i <= num) bit[i]+=val, i += i&(-i);
}

int get(int l, int r){
    int ans = 0; l--;
    while (r > 0) ans += bit[r], r -= r&(-r);
    while (l > 0) ans -= bit[l], l -= l&(-l);
    return ans;
}

void init(){
    cin >> w >> h >> n;
    for (int i = 1; i <= n; i++) cin >> x[0][i] >> y[0][i] >> x[1][i] >> y[1][i];
    x[0][n+1] = 0; y[0][n+1] = 0; x[1][n+1] = w; y[1][n+1] = 0;
    x[0][n+2] = 0; y[0][n+2] = h; x[1][n+2] = w; y[1][n+2] = h;
    x[0][n+3] = 0; y[0][n+3] = 0; x[1][n+3] = 0; y[1][n+3] = h;
    x[0][n+4] = w; y[0][n+4] = 0; x[1][n+4] = w; y[1][n+4] = h;
    n += 4;
    for (int i = 1; i <= n; i++){
        if (x[0][i] > x[1][i] || y[0][i] > y[1][i]){
            swap(x[0][i], x[1][i]);
            swap(y[0][i], y[1][i]);
        }
        if (x[0][i] != x[1][i]) {
            mv.push_back(iii(ii(x[0][i], -1), i));
            mv.push_back(iii(ii(x[1][i],  1), i));
            vc.push_back(ii(y[0][i], i));
        }
        else{
            mv.push_back(iii(ii(x[0][i],  0), i));
            vc.push_back(ii(y[0][i], i));
            vc.push_back(ii(y[1][i], i));
        }
    }
    sort (mv.begin(), mv.end());
    sort (vc.begin(), vc.end());
    dsu.init(n);
    ans = -n;
    for (int i = 0; i < vc.size(); i++){
        if (i == 0 || (i > 0 && vc[i].first != vc[i-1].first)) num++;
        int z = vc[i].second;
        if (pos[0][z]) pos[1][z] = num;
        else pos[0][z] = num;
    }
    root_ver[++ver] = ++cnt; init(1, num);
}

int main(){
    ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    init();
    for (int i = 0; i < mv.size(); i++){
        int z = mv[i].second, val = mv[i].first.first;
        if (val == x[0][z] && val == x[1][z]) update(root_ver[ver], 1, num, pos[0][z], pos[1][z], z);
        else if (val == x[0][z]) update(pos[0][z], z);
        else update(pos[0][z], 0);
        if (val == x[0][z] && val == x[1][z]) ans += get(pos[0][z], pos[1][z]);
        else if (val == x[0][z]) updatebit(pos[0][z], 1);
        else updatebit(pos[0][z], -1);
    }
    for (int i = 1; i <= cnt; i++) if (lv[i].val) dfs(i);
    ans += dsu.numdsu;
    cout << ans << "\n";
}
/*
10 10 1
2 3 2 4
*/
accuracy: 0.47297297297297297
time_ms: 90
memory_kb: 25416
final_rank: 16

code_snippet: #include <bits/stdc++.h>
using namespace std;

struct Line {
    int x1, y1, x2, y2;
    Line (int x1, int y1, int x2, int y2) :
        x1(x1), y1(y1), x2(x2), y2(y2) {}
};

struct Query {
    int l, r, id;

    Query (int l, int r, int id) :
        l(l), r(r), id(id) {}

    void dbg() {
        cout << "Query " << l << ' ' << r << ' ' << id << '\n';
    }
};

struct Update {
    int p, t, id;

    Update (int p, int t, int id) :
        p(p), t(t), id(id) {}

    void dbg() {
        cout << "Update " << p << ' ' << t << ' ' << id << '\n';
    }
};

const int N = 100010;

int w, h, n, cnt;
int it[2][8 * N];
int go[2][40 * N];
int pos[40 * N];
bool visit[40 * N];
long long cntv, cnte, cntc;
vector<Line> a;
vector<int> vx, vy, G[N];
vector<Update> update[N * 2];
vector<Query> query[N * 2];

void addEdge(int u, int v) {
//    cout << u << ' ' << v << '\n';
    G[u].push_back(v), G[v].push_back(u);
}

void add(int x, int y) {
    if (pos[x]) addEdge(pos[x], y);
    pos[x] = y;
    if (visit[x]) return; visit[x] = 1;
    for (int i = 0; i < 2; ++i) {
        if (!go[i][x]) continue;
        add(go[i][x], y);
    }
}

void upd(int i, int l, int r, int p, int t, int id) {
    it[0][i] += t, it[1][i] = ++cnt;
    if (!it[0][i]) it[1][i] = 0;
    if (l == r) {
        if (t == 1) pos[it[1][i]] = id; return;
    }
    int mid = (l + r) >> 1;
    if (p <= mid) upd(i << 1, l, mid, p, t, id);
    else upd(i << 1 | 1, mid + 1, r, p, t, id);
    go[0][it[1][i]] = it[1][i << 1], go[1][it[1][i]] = it[1][i << 1 | 1];
}

int get(int i, int l, int r, int u, int v, int id) {
    if (l > v || u > r) return 0;
    if (u <= l && r <= v) {
        if (it[1][i]) add(it[1][i], id);
        return it[0][i];
    }
    int mid = (l + r) >> 1;
    return get(i << 1, l, mid, u, v, id) + get(i << 1 | 1, mid + 1, r, u, v, id);
}

void dfs(int u) {
    visit[u] = 1;
    for (auto v : G[u]) {
        if (!visit[v]) dfs(v);
    }
}

void addLine(int x1, int y1, int x2, int y2) {
    a.push_back(Line(x1, y1, x2, y2));
    vx.push_back(x1), vx.push_back(x2);
    vy.push_back(y1), vy.push_back(y2);
}

int main() {
    ios::sync_with_stdio(false);
    cin >> w >> h >> n;

    addLine(0, 0, 0, h);
    addLine(0, h, w, h);
    addLine(w, 0, w, h);
    addLine(0, 0, w, 0);

    for (int i = 0; i < n; ++i) {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        addLine(x1, y1, x2, y2);
    }

    n += 4, cntv = n * 2, cnte = cnt = n;
    sort(vx.begin(), vx.end()), sort(vy.begin(), vy.end());

    for (int i = 0; i < n; ++i) {
        a[i].x1 = lower_bound(vx.begin(), vx.end(), a[i].x1) - vx.begin();
        a[i].y1 = lower_bound(vy.begin(), vy.end(), a[i].y1) - vy.begin();
        a[i].x2 = lower_bound(vx.begin(), vx.end(), a[i].x2) - vx.begin();
        a[i].y2 = lower_bound(vy.begin(), vy.end(), a[i].y2) - vy.begin();

        if (a[i].y1 == a[i].y2) {
            update[a[i].x1].push_back(Update(a[i].y1, 1, i + 1));
            update[a[i].x2 + 1].push_back(Update(a[i].y2, -1, i + 1));
        }
        else {
            query[a[i].x1].push_back(Query(a[i].y1, a[i].y2, i + 1));
        }
    }

    for (int i = 0; i < n * 2; ++i) {
        for (auto j : update[i]) {
            if (j.t == -1) {
//                j.dbg();
                upd(1, 0, n * 2, j.p, j.t, j.id);
            }
        }
        for (auto j : update[i]) {
            if (j.t ==  1) {
//                j.dbg();
                upd(1, 0, n * 2, j.p, j.t, j.id);
            }
        }
        for (auto j : query[i]) {
//            j.dbg();
            int tmp = get(1, 0, n * 2, j.l, j.r, j.id);
            cntv += tmp, cnte += tmp * 2;
        }
    }
    memset(visit, 0, sizeof(visit));
    for (int i = 1; i <= n; ++i) {
        if (!visit[i]) dfs(i), cntc++;
    }

    //cout << "End " << cntc << ' ' << cntv << ' ' << cnte << '\n';
    cout << cntc - cntv + cnte << '\n';
}
accuracy: 1
time_ms: 270
memory_kb: 81308
final_rank: 8

code_snippet: #include <bits/stdc++.h>
using namespace std;

struct Line {
    int x1, y1, x2, y2;
    Line (int x1, int y1, int x2, int y2) :
        x1(x1), y1(y1), x2(x2), y2(y2) {}
};

struct Query {
    int l, r, id;

    Query (int l, int r, int id) :
        l(l), r(r), id(id) {}

    void dbg() {
        cout << "Query " << l << ' ' << r << ' ' << id << '\n';
    }
};

struct Update {
    int p, t, id;

    Update (int p, int t, int id) :
        p(p), t(t), id(id) {}

    void dbg() {
        cout << "Update " << p << ' ' << t << ' ' << id << '\n';
    }
};

const int N = 100005;

int w, h, n;
int it[2][8 * N];
int go[2][40 * N];
int pos[40 * N];
bool visit[40 * N];
int cnt, cntv, cnte, cntc;
vector<Line> a;
vector<int> vx, vy, G[N];
vector<Update> update[N * 2];
vector<Query> query[N * 2];

void addEdge(int u, int v) {
    //cout << u << ' ' << v << '\n';
    G[u].push_back(v), G[v].push_back(u);
}

void add(int x, int y) {
    if (pos[x]) addEdge(pos[x], y);
    pos[x] = y;
    if (visit[x]) return; visit[x] = 1;
    for (int i = 0; i < 2; ++i) {
        if (!go[i][x]) continue;
        add(go[i][x], y);
    }
}

void upd(int i, int l, int r, int p, int t, int id) {
    it[0][i] += t, it[1][i] = ++cnt;
    if (!it[0][i]) it[1][i] = 0;
    if (l == r) {
        if (t == 1) pos[it[1][i]] = id; return;
    }
    int mid = (l + r) >> 1;
    if (p <= mid) upd(i << 1, l, mid, p, t, id);
    else upd(i << 1 | 1, mid + 1, r, p, t, id);
    go[0][it[1][i]] = it[1][i << 1], go[1][it[1][i]] = it[1][i << 1 | 1];
}

int get(int i, int l, int r, int u, int v, int id) {
    if (l > v || u > r) return 0;
    if (u <= l && r <= v) {
        if (it[1][i]) add(it[1][i], id);
        return it[0][i];
    }
    int mid = (l + r) >> 1;
    return get(i << 1, l, mid, u, v, id) + get(i << 1 | 1, mid + 1, r, u, v, id);
}

void dfs(int u) {
    visit[u] = 1;
    for (auto v : G[u]) {
        if (!visit[v]) dfs(v);
    }
}

void addLine(int x1, int y1, int x2, int y2) {
    a.push_back(Line(x1, y1, x2, y2));
    vx.push_back(x1), vx.push_back(x2);
    vy.push_back(y1), vy.push_back(y2);
}

int main() {
//    freopen("in.txt", "r", stdin);
//    freopen("out.txt", "w", stdout);
    ios::sync_with_stdio(false);
    cin >> w >> h >> n;

    addLine(0, 0, 0, h);
    addLine(0, h, w, h);
    addLine(w, 0, w, h);
    addLine(0, 0, w, 0);

    for (int i = 0; i < n; ++i) {
        int x1, y1, x2, y2;
        cin >> x1 >> y1 >> x2 >> y2;
        addLine(x1, y1, x2, y2);
    }

    n += 4, cntv = n * 2, cnte = cnt = n;
    sort(vx.begin(), vx.end()), sort(vy.begin(), vy.end());

    for (int i = 0; i < n; ++i) {
        a[i].x1 = lower_bound(vx.begin(), vx.end(), a[i].x1) - vx.begin();
        a[i].y1 = lower_bound(vy.begin(), vy.end(), a[i].y1) - vy.begin();
        a[i].x2 = lower_bound(vx.begin(), vx.end(), a[i].x2) - vx.begin();
        a[i].y2 = lower_bound(vy.begin(), vy.end(), a[i].y2) - vy.begin();

        if (a[i].y1 == a[i].y2) {
            update[a[i].x1].push_back(Update(a[i].y1, 1, i + 1));
            update[a[i].x2 + 1].push_back(Update(a[i].y2, -1, i + 1));
        }
        else {
            query[a[i].x1].push_back(Query(a[i].y1, a[i].y2, i + 1));
        }
    }

    for (int i = 0; i < n * 2; ++i) {
        for (auto j : update[i]) {
            if (j.t == -1) {
                //j.dbg();
                upd(1, 0, n * 2, j.p, j.t, j.id);
            }
        }
        for (auto j : update[i]) {
            if (j.t ==  1) {
                //j.dbg();
                upd(1, 0, n * 2, j.p, j.t, j.id);
            }
        }
        for (auto j : query[i]) {
            //j.dbg();
            int tmp = get(1, 0, n * 2, j.l, j.r, j.id);
            cntv += tmp, cnte += tmp * 2;
        }
    }

    memset(visit, 0, sizeof(visit));
    for (int i = 1; i <= n; ++i) {
        if (!visit[i]) dfs(i), cntc++;
    }

    //cout << "End " << cntc << ' ' << cntv << ' ' << cnte << '\n';
    cout << cntc - cntv + cnte << '\n';
}
accuracy: 0.6351351351351351
time_ms: 270
memory_kb: 74336
final_rank: 13

code_snippet: #include <bits/stdc++.h>
using namespace std;

#define fi first
#define se second
#define pb push_back
#define y1 y233
using ll = long long;
using pii = pair <int, int>;

const int N = 1e5L + 11;
const int M = N * 4 + 11;
const int inf = 2e9L;

int x1[N], y1[N], x2[N], y2[N];

int lowbit(int x) { return x & (-x); }
struct BIT {
	int b[M];
	void clear() { fill(b, b + M, 0); }
	void change(int p, int x) {
		for(p += 5; p < M; p += lowbit(p))
			b[p] += x;
	}
	int query(int p) {
		int r = 0;
		for(p += 5; p; p -= lowbit(p))
			r += b[p];
		return r;
	}
}bit;

struct event {
	int p, type, k1, k2, id;
	bool operator <(event e) const {
		return tie(p, type, k1, k2, id) < tie(e.p, e.type, e.k1, e.k2, e.id);
	}
};

int p[N];
int find(int x) {
	if(p[x] == x) return x;
	return p[x] = find(p[x]);
}

int main() {
	ios :: sync_with_stdio(false);
	int w, h, n; cin >> w >> h >> n;
	for(int i = 1; i <= n; i ++)
		cin >> x1[i] >> y1[i] >> x2[i] >> y2[i];
	n ++; x1[n] = 0; y1[n] = 0; x2[n] = w; y2[n] = 0;
	n ++; x1[n] = 0; y1[n] = 0; x2[n] = 0; y2[n] = h;
	n ++; x1[n] = w; y1[n] = h; x2[n] = w; y2[n] = 0;
	n ++; x1[n] = w; y1[n] = h; x2[n] = 0; y2[n] = h;
	
	{
		map <int, int> mp;
		for(int i = 1; i <= n; i ++) {
			mp[x1[i]] = 0; mp[y1[i]] = 0;
			mp[x2[i]] = 0; mp[y2[i]] = 0;
		}
		int cnt = 0;
		for(auto &p : mp)
			p.second = ++ cnt;
		for(int i = 1; i <= n; i ++) {
			//swap(x1[i], y1[i]); swap(x2[i], y2[i]);
			x1[i] = mp[x1[i]]; y1[i] = mp[y1[i]];
			x2[i] = mp[x2[i]]; y2[i] = mp[y2[i]];
		}
	}
	
	vector <event> eves; int cnt = 0;
	for(int i = 1; i <= n; i ++)
		if(x1[i] == x2[i]) {
			if(y1[i] > y2[i]) swap(y1[i], y2[i]);
			eves.pb({y1[i], 0, x1[i], 1, ++ cnt});
			eves.pb({y2[i] + 1, 0, x1[i], -1, cnt});
		} else {
			if(x1[i] > x2[i]) swap(x1[i], x2[i]);
			eves.pb({y1[i], 1, x1[i], x2[i], 0});
		}
	iota(p, p + cnt + 1, 0);
	sort(eves.begin(), eves.end());

	set <pii> sts, stp;
	for(int i = 0; i < M; i ++)
		sts.insert({i, i});
	stp.insert({M, 0});

	ll m = 0, sp = 0;
	bit.clear();
	for(auto e : eves)
		if(e.type == 0) {
			bit.change(e.k1, e.k2);

			if(e.k2 == 1) {
				stp.insert({e.k1, e.id});
				pii p = *(-- sts.lower_bound({e.k1, inf})); sts.erase(p);
				if(p.fi != e.k1) sts.insert({p.fi, e.k1 - 1});
				sts.insert({e.k1, e.k1});
				if(p.se != e.k1) sts.insert({e.k1 + 1, p.se});
			} else
				stp.erase({e.k1, e.id});
		} else {
			m += bit.query(e.k2);
			m -= bit.query(e.k1 - 1);

			auto it = stp.lower_bound({e.k1, 0});
			if(it -> first > e.k2) { sp ++; continue; }
			int x = it -> second;

			it = -- sts.lower_bound({e.k1, inf});

			if(it -> fi != e.k1) {
				pii p = *it; sts.erase(it);
				sts.insert({p.fi, e.k1 - 1});
				sts.insert({e.k1, p.se});
				it = -- sts.lower_bound({e.k1, inf});
			}
			int l = it -> fi, r;

			while(it -> fi <= e.k2) {
				if(it -> se > e.k2) {
					pii p = *it; sts.erase(it);
					sts.insert({p.fi, e.k2});
					sts.insert({e.k2 + 1, p.se});
					it = -- sts.lower_bound({p.fi, inf});
				}

				r = it -> se;
				auto is = stp.lower_bound({it -> fi, 0});
				if(is -> fi <= it -> se) {
					int y = is -> se;
					int a = find(x), b = find(y);
					if(a != b) p[b] = a;
				}
				is = it ++;
				sts.erase(is);
			}
			sts.insert({l, r});
		}
	ll ans = m - n + sp;
	for(int i = 1; i <= cnt; i ++)
		if(find(i) == i) ans ++;
	cout << ans << '\n';
}
accuracy: 1
time_ms: 490
memory_kb: 30132
final_rank: 6

code_snippet: #include <bits/stdc++.h>
using namespace std;

#define fi first
#define se second
#define pb push_back
#define y1 y233
using ll = long long;
using pii = pair <int, int>;

const int N = 1e5L + 11;
const int M = N * 4 + 11;
const int inf = 2e9L;

int x1[N], y1[N], x2[N], y2[N];

int lowbit(int x) { return x & (-x); }
struct BIT {
	int b[M];
	void clear() { fill(b, b + M, 0); }
	void change(int p, int x) {
		for(p += 5; p < M; p += lowbit(p))
			b[p] += x;
	}
	int query(int p) {
		int r = 0;
		for(p += 5; p; p -= lowbit(p))
			r += b[p];
		return r;
	}
}bit;

struct event {
	int p, type, k1, k2, id;
	bool operator <(event e) const {
		return tie(p, type, k1, k2, id) < tie(e.p, e.type, e.k1, e.k2, e.id);
	}
};

int p[N];
int find(int x) {
	if(p[x] == x) return x;
	return p[x] = find(p[x]);
}

int main() {
	ios :: sync_with_stdio(false);
	int w, h, n; cin >> w >> h >> n;
	for(int i = 1; i <= n; i ++)
		cin >> x1[i] >> y1[i] >> x2[i] >> y2[i];
	n ++; x1[n] = 0; y1[n] = 0; x2[n] = w; y2[n] = 0;
	n ++; x1[n] = 0; y1[n] = 0; x2[n] = 0; y2[n] = h;
	n ++; x1[n] = w; y1[n] = h; x2[n] = w; y2[n] = 0;
	n ++; x1[n] = w; y1[n] = h; x2[n] = 0; y2[n] = h;
	
	{
		map <int, int> mp;
		for(int i = 1; i <= n; i ++) {
			mp[x1[i]] = 0; mp[y1[i]] = 0;
			mp[x2[i]] = 0; mp[y2[i]] = 0;
		}
		int cnt = 0;
		for(auto &p : mp)
			p.second = ++ cnt;
		for(int i = 1; i <= n; i ++) {
			//swap(x1[i], y1[i]); swap(x2[i], y2[i]);
			x1[i] = mp[x1[i]]; y1[i] = mp[y1[i]];
			x2[i] = mp[x2[i]]; y2[i] = mp[y2[i]];
		}
	}
	
	vector <event> eves; int cnt = 0;
	for(int i = 1; i <= n; i ++)
		if(x1[i] == x2[i]) {
			if(y1[i] > y2[i]) swap(y1[i], y2[i]);
			eves.pb({y1[i], 0, x1[i], 1, ++ cnt});
			eves.pb({y2[i] + 1, 0, x1[i], -1, cnt});
		} else {
			if(x1[i] > x2[i]) swap(x1[i], x2[i]);
			eves.pb({y1[i], 1, x1[i], x2[i], 0});
		}
	iota(p, p + cnt + 1, 0);
	sort(eves.begin(), eves.end());

	set <pii> sts, stp;
	for(int i = 0; i < M; i ++)
		sts.insert({i, i});
	stp.insert({M, 0});

	ll m = 0, sp = 0;
	bit.clear();
	for(auto e : eves)
		if(e.type == 0) {
			bit.change(e.k1, e.k2);

			if(e.k2 == 1) {
				stp.insert({e.k1, e.id});
				pii p = *(-- sts.lower_bound({e.k1, inf})); sts.erase(p);
				if(p.fi != e.k1) sts.insert({p.fi, e.k1 - 1});
				sts.insert({e.k1, e.k1});
				if(p.se != e.k1) sts.insert({e.k1 + 1, p.se});
			} else {
				stp.erase({e.k1, e.id});
			}
		} else {
			m += bit.query(e.k2);
			m -= bit.query(e.k1 - 1);

			auto it = stp.lower_bound({e.k1, 0});
			if(it -> first > e.k2) { sp ++; continue; }
			int x = it -> second;

			it = -- sts.lower_bound({e.k1, inf});
			int l = it -> first, r = it -> second;
			while(1) {
				r = it -> second;
				auto is = stp.lower_bound({it -> first, 0});
				if(is -> first <= it -> second) {
					int y = is -> second;
					int a = find(x), b = find(y);
					if(a != b) p[b] = a;
				}

				is = it ++;
				if(is -> second >= e.k2) { sts.erase(is); break; }
				sts.erase(is);
			}
			sts.insert({l, r});
		}
	int ans = m - n + sp;
	for(int i = 1; i <= cnt; i ++)
		if(find(i) == i) ans ++;
	cout << ans << '\n';
}
accuracy: 0.0945945945945946
time_ms: 90
memory_kb: 23524
final_rank: 17

code_snippet: #include <bits/stdc++.h>
using namespace std;

#define fi first
#define se second
#define pb push_back
#define y1 y233
using ll = long long;
using pii = pair <int, int>;

const int N = 1e5L + 11;
const int M = N * 4 + 11;
const int inf = 2e9L;

int x1[N], y1[N], x2[N], y2[N];

int lowbit(int x) { return x & (-x); }
struct BIT {
	int b[M];
	void clear() { fill(b, b + M, 0); }
	void change(int p, int x) {
		for(p += 5; p < M; p += lowbit(p))
			b[p] += x;
	}
	int query(int p) {
		int r = 0;
		for(p += 5; p; p -= lowbit(p))
			r += b[p];
		return r;
	}
}bit;

struct event {
	int p, type, k1, k2, id;
	bool operator <(event e) const {
		return tie(p, type, k1, k2, id) < tie(e.p, e.type, e.k1, e.k2, e.id);
	}
};

int p[N];
int find(int x) {
	if(p[x] == x) return x;
	return p[x] = find(p[x]);
}

int main() {
	ios :: sync_with_stdio(false);
	int w, h, n; cin >> w >> h >> n;
	for(int i = 1; i <= n; i ++)
		cin >> x1[i] >> y1[i] >> x2[i] >> y2[i];
	n ++; x1[n] = 0; y1[n] = 0; x2[n] = w; y2[n] = 0;
	n ++; x1[n] = 0; y1[n] = 0; x2[n] = 0; y2[n] = h;
	n ++; x1[n] = w; y1[n] = h; x2[n] = w; y2[n] = 0;
	n ++; x1[n] = w; y1[n] = h; x2[n] = 0; y2[n] = h;
	/*
	{
		map <int, int> mp;
		for(int i = 1; i <= n; i ++) {
			mp[x1[i]] = 0; mp[y1[i]] = 0;
			mp[x2[i]] = 0; mp[y2[i]] = 0;
		}
		int cnt = 0;
		for(auto &p : mp)
			p.second = ++ cnt;
		for(int i = 1; i <= n; i ++) {
			x1[i] = mp[x1[i]]; y1[i] = mp[y1[i]];
			x2[i] = mp[x2[i]]; y2[i] = mp[y2[i]];
		}
	}
	*/
	vector <event> eves; int cnt = 0;
	for(int i = 1; i <= n; i ++)
		if(x1[i] == x2[i]) {
			if(y1[i] > y2[i]) swap(y1[i], y2[i]);
			eves.pb({y1[i], 0, x1[i], 1, ++ cnt});
			eves.pb({y2[i] + 1, 0, x1[i], -1, cnt});
		} else {
			if(x1[i] > x2[i]) swap(x1[i], x2[i]);
			eves.pb({y1[i], 1, x1[i], x2[i], 0});
		}
	iota(p, p + cnt + 1, 0);
	sort(eves.begin(), eves.end());

	set <pii> sts, stp;
	for(int i = 0; i < M; i ++)
		sts.insert({i, i});
	stp.insert({M, 0});

	ll m = 0, sp = 0;
	bit.clear();
	for(auto e : eves)
		if(e.type == 0) {
			bit.change(e.k1, e.k2);

			if(e.k2 == 1) {
				stp.insert({e.k1, e.id});
				pii p = *(-- sts.lower_bound({e.k1, inf})); sts.erase(p);
				if(p.fi != e.k1) sts.insert({p.fi, e.k1 - 1});
				sts.insert({e.k1, e.k1});
				if(p.se != e.k1) sts.insert({e.k1 + 1, p.se});
			} else {
				stp.erase({e.k1, e.id});
			}
		} else {
			m += bit.query(e.k2);
			m -= bit.query(e.k1 - 1);

			auto it = stp.lower_bound({e.k1, 0});
			if(it -> first > e.k2) { sp ++; continue; }
			int x = it -> second;

			it = -- sts.lower_bound({e.k1, inf});
			int l = it -> first, r = it -> second;
			while(1) {
				auto is = stp.lower_bound({it -> first, 0});
				if(is -> first <= it -> second) {
					int y = is -> second;
					int a = find(x), b = find(y);
					if(a != b) p[b] = a;
				}

				is = it ++;
				r = it -> second;
				if(is -> second >= e.k2) { sts.erase(is); break; }
				sts.erase(is);
			}
			sts.insert({l, r});
		}
	int ans = m - n + sp;
	for(int i = 1; i <= cnt; i ++)
		if(find(i) == i) ans ++;
	cout << ans << '\n';
}
accuracy: 0.06756756756756757
time_ms: 90
memory_kb: 23488
final_rank: 18

code_snippet: #include <bits/stdc++.h>
using namespace std;

#define fi first
#define se second
#define pb push_back
#define y1 y233
using ll = long long;
using pii = pair <int, int>;

const int N = 1e5L + 11;
const int M = N * 4 + 11;

int x1[N], y1[N], x2[N], y2[N];

int lowbit(int x) { return x & (-x); }
struct BIT {
	int b[M];
	void clear() { fill(b, b + M, 0); }
	void change(int p, int x) {
		for(p += 5; p < M; p += lowbit(p))
			b[p] += x;
	}
	int query(int p) {
		int r = 0;
		for(p += 5; p; p -= lowbit(p))
			r += b[p];
		return r;
	}
}bit;

struct event {
	int p, type, k1, k2, id;
	bool operator <(event e) const {
		return tie(p, type, k1, k2, id) < tie(e.p, e.type, e.k1, e.k2, e.id);
	}
};

int p[N];
int find(int x) {
	if(p[x] == x) return x;
	return p[x] = find(p[x]);
}

int main() {
	ios :: sync_with_stdio(false);
	int w, h, n; cin >> w >> h >> n;
	for(int i = 1; i <= n; i ++)
		cin >> x1[i] >> y1[i] >> x2[i] >> y2[i];
	n ++; x1[n] = 0; y1[n] = 0; x2[n] = w; y2[n] = 0;
	n ++; x1[n] = 0; y1[n] = 0; x2[n] = 0; y2[n] = h;
	n ++; x1[n] = w; y1[n] = h; x2[n] = w; y2[n] = 0;
	n ++; x1[n] = w; y1[n] = h; x2[n] = 0; y2[n] = h;
	{
		map <int, int> mp;
		for(int i = 1; i <= n; i ++) {
			mp[x1[i]] = 0; mp[y1[i]] = 0;
			mp[x2[i]] = 0; mp[y2[i]] = 0;
		}
		int cnt = 0;
		for(auto &p : mp)
			p.second = ++ cnt;
		for(int i = 1; i <= n; i ++) {
			x1[i] = mp[x1[i]]; y1[i] = mp[y1[i]];
			x2[i] = mp[x2[i]]; y2[i] = mp[y2[i]];
		}
	}
	vector <event> eves; int cnt = 0;
	for(int i = 1; i <= n; i ++)
		if(x1[i] == x2[i]) {
			if(y1[i] > y2[i]) swap(y1[i], y2[i]);
			eves.pb({y1[i], 0, x1[i], 1, ++ cnt});
			eves.pb({y2[i] + 1, 0, x1[i], -1, cnt});
		} else {
			if(x1[i] > x2[i]) swap(x1[i], x2[i]);
			eves.pb({y1[i], 1, x1[i], x2[i], 0});
		}
	iota(p, p + cnt + 1, 0);
	sort(eves.begin(), eves.end());

	set <pii> sts, stp;
	for(int i = 0; i < M; i ++)
		sts.insert({i, i});
	stp.insert({M, 0});

	ll m = 0;
	bit.clear();
	for(auto e : eves)
		if(e.type == 0) {
			bit.change(e.k1, e.k2);

			if(e.k2 == 1) {
				stp.insert({e.k1, e.id});
				pii p = *sts.lower_bound({e.k1, 0}); sts.erase(p);
				if(p.fi != e.k1) sts.insert({p.fi, e.k1 - 1});
				sts.insert({e.k1, e.k1});
				if(p.se != e.k1) sts.insert({e.k1 + 1, p.se});
			} else {
				stp.erase({e.k1, e.id});
			}
		} else {
			m += bit.query(e.k2);
			m -= bit.query(e.k1 - 1);

			auto it = stp.lower_bound({e.k1, 0});
			if(it -> first > e.k2) { m ++; continue; }
			int x = it -> second;

			it = sts.lower_bound({e.k1, 0});
			if(e.k1 != it -> fi) it --;
			int l = it -> first, r = it -> second;
			while(1) {
				auto is = stp.lower_bound({it -> first, 0});
				if(is -> first <= it -> second) {
					int y = is -> second;
					int a = find(x), b = find(y);
					if(a != b) p[b] = a;
				}

				is = it ++;
				r = it -> second;
				if(is -> second >= e.k2) { sts.erase(is); break; }
				sts.erase(is);
			}
			sts.insert({l, r});
		}
	int ans = m - n;
	for(int i = 1; i <= cnt; i ++)
		if(find(i) == i) ans ++;
	cout << ans << '\n';
}
accuracy: 0.04054054054054054
time_ms: 80
memory_kb: 23532
final_rank: 19

code_snippet: #include <bits/stdc++.h>
using namespace std;

#define fi first
#define se second
#define pb push_back
#define y1 y233
using ll = long long;
using pii = pair <int, int>;

const int N = 1e5L + 11;
const int M = N * 4 + 11;

int x1[N], y1[N], x2[N], y2[N];

int lowbit(int x) { return x & (-x); }
struct BIT {
	int b[M];
	void clear() { fill(b, b + M, 0); }
	void change(int p, int x) {
		for(p += 5; p < M; p += lowbit(p))
			b[p] += x;
	}
	int query(int p) {
		int r = 0;
		for(p += 5; p; p -= lowbit(p))
			r += b[p];
		return r;
	}
}bit;

struct event {
	int p, type, k1, k2, id;
	bool operator <(event e) const {
		return tie(p, type, k1, k2, id) < tie(e.p, e.type, e.k1, e.k2, e.id);
	}
};

int p[N];
int find(int x) {
	if(p[x] == x) return x;
	return p[x] = find(p[x]);
}

int main() {
	ios :: sync_with_stdio(false);
	int w, h, n; cin >> w >> h >> n;
	for(int i = 1; i <= n; i ++)
		cin >> x1[i] >> y1[i] >> x2[i] >> y2[i];
	n ++; x1[n] = 0; y1[n] = 0; x2[n] = w; y2[n] = 0;
	n ++; x1[n] = 0; y1[n] = 0; x2[n] = 0; y2[n] = h;
	n ++; x1[n] = w; y1[n] = h; x2[n] = w; y2[n] = 0;
	n ++; x1[n] = w; y1[n] = h; x2[n] = 0; y2[n] = h;
	{
		map <int, int> mp;
		for(int i = 1; i <= n; i ++) {
			mp[x1[i]] = 0; mp[y1[i]] = 0;
			mp[x2[i]] = 0; mp[y2[i]] = 0;
		}
		int cnt = 0;
		for(auto &p : mp)
			p.second = ++ cnt;
		for(int i = 1; i <= n; i ++) {
			x1[i] = mp[x1[i]]; y1[i] = mp[y1[i]];
			x2[i] = mp[x2[i]]; y2[i] = mp[y2[i]];
		}
	}
	vector <event> eves; int cnt = 0;
	for(int i = 1; i <= n; i ++)
		if(x1[i] == x2[i]) {
			if(y1[i] > y2[i]) swap(y1[i], y2[i]);
			eves.pb({y1[i], 0, x1[i], 1, ++ cnt});
			eves.pb({y2[i] + 1, 0, x1[i], -1, cnt});
		} else {
			if(x1[i] > x2[i]) swap(x1[i], x2[i]);
			eves.pb({y1[i], 1, x1[i], x2[i], 0});
		}
	iota(p, p + cnt + 1, 0);
	sort(eves.begin(), eves.end());

	set <pii> sts, stp;
	for(int i = 0; i < M; i ++)
		sts.insert({i, i});
	stp.insert({M, 0});

	ll m = 0;
	bit.clear();
	for(auto e : eves)
		if(e.type == 0) {
			bit.change(e.k1, e.k2);

			if(e.k2 == 1) {
				stp.insert({e.k1, e.id});
				pii p = *sts.lower_bound({e.k1, 0}); sts.erase(p);
				if(p.fi != e.k1) sts.insert({p.fi, e.k1 - 1});
				sts.insert({e.k1, e.k1});
				if(p.se != e.k1) sts.insert({e.k1 + 1, p.se});
			} else {
				stp.erase({e.k1, e.id});
			}
		} else {
			m += bit.query(e.k2);
			m -= bit.query(e.k1 - 1);

			auto it = stp.lower_bound({e.k1, 0});
			if(it -> first > e.k2) continue;
			int x = it -> second;

			it = sts.lower_bound({e.k1, 0});
			if(e.k1 != it -> fi) it --;
			int l = it -> first, r = it -> second;
			while(1) {
				auto is = stp.lower_bound({it -> first, 0});
				if(is -> first <= it -> second) {
					int y = is -> second;
					int a = find(x), b = find(y);
					if(a != b) p[b] = a;
				}

				is = it ++;
				r = it -> second;
				if(is -> second >= e.k2) { sts.erase(is); break; }
				sts.erase(is);
			}
			sts.insert({l, r});
		}
	int ans = m - n;
	for(int i = 1; i <= cnt; i ++)
		if(find(i) == i) ans ++;
	cout << ans << '\n';
}
accuracy: 0.02702702702702703
time_ms: 80
memory_kb: 23492
final_rank: 20