Skip to content

Commit

Permalink
add centroid_decomposition
Browse files Browse the repository at this point in the history
  • Loading branch information
idat50me committed Jul 16, 2024
1 parent 53c9710 commit 383aa39
Show file tree
Hide file tree
Showing 3 changed files with 156 additions and 0 deletions.
68 changes: 68 additions & 0 deletions graph/centroid_decomposition.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#pragma once

#ifndef call_include
#define call_include
#include <bits/stdc++.h>
using namespace std;
#endif

#include "../structure/2d_array.cpp"

struct centroid_decomposition {
private:
int n;
v2d<int> &E;
vector<int> deleted;
vector<int> subsizes;
int found_cent;

int centroid = -1;
vector<pair<int, int>> subtrees;

public:
centroid_decomposition(v2d<int> &E) : E(E), n(E.size()), deleted(n, 0), subsizes(n){};

private:
int dfs(int x, int tsize, int prev = -1) {
if(found_cent) return -1;

int size = 1, cent_flag = 1;
for(int i = 0; i < E[x].size(); i++) {
int nx = E[x][i];
if(deleted[nx] or nx == prev) continue;
int chsize = dfs(nx, tsize, x);
if(found_cent) return -1;
if(chsize > tsize / 2) cent_flag = 0;
size += chsize;
}
if(tsize - size > tsize / 2) cent_flag = 0;
if(cent_flag) {
found_cent = 1;
centroid = x;
for(int i = 0; i < E[x].size(); i++) {
int nx = E[x][i];
if(deleted[nx]) continue;
if(nx == prev) {
subtrees.emplace_back(nx, tsize - size);
}
else {
subtrees.emplace_back(nx, subsizes[nx]);
}
}
}

return subsizes[x] = size;
}

public:
pair<int, vector<pair<int, int>>> get(int x, int tsize) {
subtrees.clear();
found_cent = 0;
dfs(x, tsize);
return pair(centroid, subtrees);
}

inline void del(int x) {
deleted[x] = 1;
}
};
32 changes: 32 additions & 0 deletions graph/docs/centroid_decomposition.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
title: Centroid Decomposition (木の重心分解)
documentation_of: ../centroid_decomposition.cpp
---

## なにこれ
木構造の重心を求める.

## コンストラクタ
- `centroid_decomposition(E)`:木または森を表す隣接リスト `E` について前処理を行う.

## メンバ関数
- `get(x, tsize)`:頂点 `x` が含まれるサイズ `tsize` の木について,重心および重心分解後の各部分木に含まれる頂点とサイズの配列を返す.
- `del(x)`:頂点 `x` を削除する.

## 計算量
`E` に含まれる頂点数を $n$ とする.
- コンストラクタ:$O(n)$

対象となる木のサイズを $m$ とする.
- `get(x, tsize)`:$O(m)$
- `del(x)`:$O(1)$

## 備考
重心分解後の各部分木について,`get()` の返り値に含まれる頂点とサイズの情報を用いることで再帰的に重心分解を繰り返すことができる.

1レイヤーの重心分解につき部分木の最大サイズは半分以下となるため,頂点数1の木になるまで重心分解したときのレイヤー数は $O(\log n)$ である.
また,`del(x)` で重心を論理削除すれば `E` および `subsizes` を再初期化する必要はないため,1つのインスタンスを使い回すことができる.
これは全体 $O(n \log n)$ で実行することができる.

# 参考
- https://qiita.com/drken/items/4b4c3f1824339b090202
56 changes: 56 additions & 0 deletions test/cf_321c.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// competitive-verifier: PROBLEM https://codeforces.com/problemset/problem/321/C
// competitive-verifier: IGNORE
// non-supported site

#ifndef call_include
#define call_include
#include <bits/stdc++.h>
using namespace std;
#endif

#include "structure/2d_array.cpp"
#include "graph/centroid_decomposition.cpp"

int main() {
int N;
v2d<int> E;

cin >> N;
E.assign(N, 0);
for(int i = 0; i < N - 1; i++) {
int a, b;
cin >> a >> b;
a--, b--;
E[a].emplace_back(b);
E[b].emplace_back(a);
}

vector<char> ans(N);
char c = 'A';
queue<int> q, qs;
auto cd = centroid_decomposition(E);
q.push(0);
qs.push(N);
while(not q.empty()) {
queue<int> q2, qs2;
while(not q.empty()) {
int x = q.front(), xs = qs.front();
q.pop(), qs.pop();

auto res = cd.get(x, xs);
int centroid = res.first;
auto v = res.second;
ans[centroid] = c;
cd.del(centroid);
for(int i = 0; i < v.size(); i++) {
q2.push(v[i].first);
qs2.push(v[i].second);
}
}
swap(q, q2);
swap(qs, qs2);
c++;
}

for(int i = 0; i < N; i++) cout << ans[i] << (i < N - 1 ? ' ' : '\n');
}

0 comments on commit 383aa39

Please sign in to comment.