TOP > データ構造
Link-Cut木 部分木クエリ(Link-Cut-Tree-Subtree)
説明
がんばると、Link-Cut木で部分木に対するクエリも処理できるようになる。
普通のLink-Cut木と同様に遅延評価もできる気がするが, そのような問題を見たことないので未実装。
計算量
- クエリ $O(\log N)$
実装例
テンプレートの第一引数には、載せたいデータを定義した構造体を渡す。構造体では、以下の関数を定義すること。基本的にメンバ変数には部分木全体のdata、Light-Edgeのみのdata($key$とLight-Edgeのみのdataと$parent$と$child$を使ってマージしたものが部分木全体のdataとなるイメージ) が必要。
- merge($key$, $parent$, $child$) := そのノードの値 $key$ と、$parent$(親, 左部分木), $child$(子, 右部分木) をマージする。
- toggle() := そのノードの親と子を入れ替える。
- add($child$) := $child$ をLight-Edgeでそのノードに繋ぐ
- erase($child$) := Light-Edge で繋がっていた $child$ をそのノードから削除する
template< typename SUM, typename KEY >
struct LinkCutTreeSubtree {
struct Node {
Node *l, *r, *p;
KEY key;
SUM sum;
bool rev;
int sz;
bool is_root() const {
return !p || (p->l != this && p->r != this);
}
Node(const KEY &key, const SUM &sum) :
key(key), sum(sum), rev(false), sz(1),
l(nullptr), r(nullptr), p(nullptr) {}
};
const SUM ident;
LinkCutTreeSubtree(const SUM &ident) : ident(ident) {}
Node *make_node(const KEY &key) {
auto ret = new Node(key, ident);
update(ret);
return ret;
}
Node *set_key(Node *t, const KEY &key) {
expose(t);
t->key = key;
update(t);
return t;
}
void toggle(Node *t) {
swap(t->l, t->r);
t->sum.toggle();
t->rev ^= true;
}
void push(Node *t) {
if(t->rev) {
if(t->l) toggle(t->l);
if(t->r) toggle(t->r);
t->rev = false;
}
}
void update(Node *t) {
t->sz = 1;
if(t->l) t->sz += t->l->sz;
if(t->r) t->sz += t->r->sz;
t->sum.merge(t->key, t->l ? t->l->sum : ident, t->r ? t->r->sum : ident);
}
void rotr(Node *t) {
auto *x = t->p, *y = x->p;
if((x->l = t->r)) t->r->p = x;
t->r = x, x->p = t;
update(x), update(t);
if((t->p = y)) {
if(y->l == x) y->l = t;
if(y->r == x) y->r = t;
update(y);
}
}
void rotl(Node *t) {
auto *x = t->p, *y = x->p;
if((x->r = t->l)) t->l->p = x;
t->l = x, x->p = t;
update(x), update(t);
if((t->p = y)) {
if(y->l == x) y->l = t;
if(y->r == x) y->r = t;
update(y);
}
}
void splay(Node *t) {
push(t);
while(!t->is_root()) {
auto *q = t->p;
if(q->is_root()) {
push(q), push(t);
if(q->l == t) rotr(t);
else rotl(t);
} else {
auto *r = q->p;
push(r), push(q), push(t);
if(r->l == q) {
if(q->l == t) rotr(q), rotr(t);
else rotl(t), rotr(t);
} else {
if(q->r == t) rotl(q), rotl(t);
else rotr(t), rotl(t);
}
}
}
}
Node *expose(Node *t) {
Node *rp = nullptr;
for(auto *cur = t; cur; cur = cur->p) {
splay(cur);
if(cur->r) cur->sum.add(cur->r->sum);
cur->r = rp;
if(cur->r) cur->sum.erase(cur->r->sum);
update(cur);
rp = cur;
}
splay(t);
return rp;
}
void link(Node *child, Node *parent) {
expose(child);
expose(parent);
child->p = parent;
parent->r = child;
update(parent);
}
void cut(Node *child) {
expose(child);
auto *parent = child->l;
child->l = nullptr;
parent->p = nullptr;
update(child);
}
void evert(Node *t) {
expose(t);
toggle(t);
push(t);
}
Node *lca(Node *u, Node *v) {
if(get_root(u) != get_root(v)) return nullptr;
expose(u);
return expose(v);
}
Node *get_kth(Node *x, int k) {
expose(x);
while(x) {
push(x);
if(x->r && x->r->sz > k) {
x = x->r;
} else {
if(x->r) k -= x->r->sz;
if(k == 0) return x;
k -= 1;
x = x->l;
}
}
return nullptr;
}
Node *get_root(Node *x) {
expose(x);
while(x->l) {
push(x);
x = x->l;
}
return x;
}
SUM &query(Node *t) {
expose(t);
return t->sum;
}
};
検証
Codeforces Round #564 (Div. 1) E. Nauuo and ODT
int main() {
struct tap {
int64 sz, szsum, cashsz, cashszsum;
tap() : sz(0), szsum(0), cashsz(0), cashszsum(0) {}
void merge(int64 key, const tap &parent, const tap &child) {
szsum = cashszsum;
szsum += parent.sz * parent.sz;
szsum += child.sz * child.sz;
sz = key;
sz += parent.sz;
sz += child.sz;
sz += cashsz;
}
void toggle() {
}
void add(const tap &child) {
cashszsum += child.sz * child.sz;
cashsz += child.sz;
}
void erase(const tap &child) {
cashszsum -= child.sz * child.sz;
cashsz -= child.sz;
}
} e;
int N, M;
cin >> N >> M;
vector< int > C(N);
cin >> C;
using pi = pair< int64, int >;
using LCT = LinkCutTreeSubtree< tap, int64 >;
LCT lct(e);
vector< LCT::Node * > sub(N);
vector< map< int, vector< pair< int, int > > > > belong(N + 1);
for(int i = 0; i < N; i++) {
--C[i];
sub[i] = lct.make_node(1);
belong[C[i]][-1].emplace_back(i, 0);
}
vector< int > parent(N + 1, -1);
vector< int > g[400001];
g[N].emplace_back(0);
sub.emplace_back(lct.make_node(1));
function< void(int, int) > dfs = [&](int idx, int par) {
parent[idx] = par;
for(auto &to : g[idx]) {
if(to == par) continue;
dfs(to, idx);
}
};
for(int i = 1; i < N; i++) {
int x, y;
cin >> x >> y;
--x, --y;
g[x].emplace_back(y);
g[y].emplace_back(x);
lct.evert(sub[y]);
lct.link(sub[y], sub[x]);
}
lct.evert(sub[0]);
lct.link(sub[0], sub[N]);
dfs(N, -1);
for(int i = 0; i < M; i++) {
int u, x;
cin >> u >> x;
--u, --x;
if(C[u] != x) {
belong[C[u]][i].emplace_back(u, 1); // set black
C[u] = x;
belong[x][i].emplace_back(u, 0); // set white
}
}
vector< int64 > ans(M + 1);
for(int i = 0; i < N; i++) {
int last = -1;
int64 path = 1LL * N * N;
set< int > st;
for(auto &vs : belong[i]) {
ans[last + 1] += path;
ans[vs.first + 1] -= path;
for(auto &p : vs.second) {
if(!p.second) {
st.emplace(p.first);
path -= lct.query(lct.get_root(sub[p.first])).szsum;
lct.cut(sub[p.first]);
path += lct.query(lct.get_root(sub[parent[p.first]])).szsum;
path += lct.query(lct.get_root(sub[p.first])).szsum;
} else {
st.erase(p.first);
path -= lct.query(lct.get_root(sub[p.first])).szsum;
path -= lct.query(lct.get_root(sub[parent[p.first]])).szsum;
lct.link(sub[p.first], sub[parent[p.first]]);
path += lct.query(lct.get_root(sub[p.first])).szsum;
}
}
last = vs.first;
}
ans[last + 1] += path;
for(auto &p : st) lct.link(sub[p], sub[parent[p]]);
}
int64 ret = 0;
for(int j = 0; j <= M; j++) {
ret += ans[j];
cout << 1LL * N * N * N - ret << "\n";
}
}
技術室奥プログラミングコンテスト J - 仕事をしよう! (Working!)
template< typename T, typename Compare = less< T > >
struct PQ {
priority_queue< T, vector< T >, Compare > que1, que2;
PQ() = default;
void push(T k) {
que1.emplace(k);
}
void pop(T k) {
que2.emplace(k);
}
inline void modify() {
while(que2.size() && que2.top() == que1.top()) {
que2.pop();
que1.pop();
}
}
bool empty() {
modify();
return que1.empty();
}
T top() {
modify();
return que1.top();
}
};
int main() {
struct Farthest {
PQ< int64 > pq;
int64 c_max, p_max, length;
Farthest() : c_max(-infll), p_max(-infll), length(0) {}
void merge(int64 key, const Farthest &parent, const Farthest &child) {
p_max = max(child.p_max, child.length + key + max(pq.empty() ? 0 : pq.top(), parent.p_max));
c_max = max(parent.c_max, parent.length + key + max(pq.empty() ? 0 : pq.top(), child.c_max));
length = parent.length + key + child.length;
}
void toggle() {
swap(c_max, p_max);
}
void add(const Farthest &child) {
pq.push(child.c_max);
}
void erase(const Farthest &child) {
pq.pop(child.c_max);
}
} e;
int Q;
cin >> Q;
using LCT = LinkCutTreeSubtree< Farthest, int64 >;
LCT lct(e);
vector< LCT::Node * > ev(500001), ee(500001);
ev[0] = lct.make_node(0);
int num = 1;
for(int i = 0; i < Q; i++) {
int t, a, b;
cin >> t >> a >> b;
if(t == 1) {
ev[num] = lct.make_node(0);
ee[num] = lct.make_node(b);
lct.link(ee[num], ev[a]);
lct.link(ev[num], ee[num]);
++num;
} else if(t == 2) {
lct.set_key(ee[a], b);
} else {
lct.evert(ev[a]);
cout << ev[a]->sum.c_max << "\n";
}
}
}
その他いろいろ QTREE LCT + Dynamic Distance Sum