Created
June 17, 2021 00:00
-
-
Save Restuta/8266d5627e1aa414b2a731094b378641 to your computer and use it in GitHub Desktop.
what is this omg (https://codingcompetitions.withgoogle.com/codejam/round/000000000019ff31/00000000003b4f31)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* author: tourist | |
* created: 08.08.2020 17:11:40 | |
**/ | |
#include <bits/stdc++.h> | |
using namespace std; | |
template <typename T> | |
class graph { | |
public: | |
struct edge { | |
int from; | |
int to; | |
T cost; | |
}; | |
vector<edge> edges; | |
vector<vector<int>> g; | |
int n; | |
graph(int _n) : n(_n) { | |
g.resize(n); | |
} | |
virtual int add(int from, int to, T cost) = 0; | |
}; | |
template <typename T> | |
class forest : public graph<T> { | |
public: | |
using graph<T>::edges; | |
using graph<T>::g; | |
using graph<T>::n; | |
forest(int _n) : graph<T>(_n) { | |
} | |
int add(int from, int to, T cost = 1) { | |
assert(0 <= from && from < n && 0 <= to && to < n); | |
int id = (int) edges.size(); | |
assert(id < n - 1); | |
g[from].push_back(id); | |
g[to].push_back(id); | |
edges.push_back({from, to, cost}); | |
return id; | |
} | |
}; | |
template <typename T> | |
class dfs_forest : public forest<T> { | |
public: | |
using forest<T>::edges; | |
using forest<T>::g; | |
using forest<T>::n; | |
vector<int> pv; | |
vector<int> pe; | |
vector<int> order; | |
vector<int> pos; | |
vector<int> end; | |
vector<int> sz; | |
vector<int> root; | |
vector<int> depth; | |
vector<T> dist; | |
dfs_forest(int _n) : forest<T>(_n) { | |
} | |
void init() { | |
pv = vector<int>(n, -1); | |
pe = vector<int>(n, -1); | |
order.clear(); | |
pos = vector<int>(n, -1); | |
end = vector<int>(n, -1); | |
sz = vector<int>(n, 0); | |
root = vector<int>(n, -1); | |
depth = vector<int>(n, -1); | |
dist = vector<T>(n); | |
} | |
void clear() { | |
pv.clear(); | |
pe.clear(); | |
order.clear(); | |
pos.clear(); | |
end.clear(); | |
sz.clear(); | |
root.clear(); | |
depth.clear(); | |
dist.clear(); | |
} | |
private: | |
void do_dfs(int v) { | |
pos[v] = (int) order.size(); | |
order.push_back(v); | |
sz[v] = 1; | |
for (int id : g[v]) { | |
if (id == pe[v]) { | |
continue; | |
} | |
auto &e = edges[id]; | |
int to = e.from ^ e.to ^ v; | |
depth[to] = depth[v] + 1; | |
dist[to] = dist[v] + e.cost; | |
pv[to] = v; | |
pe[to] = id; | |
root[to] = (root[v] != -1 ? root[v] : to); | |
do_dfs(to); | |
sz[v] += sz[to]; | |
} | |
end[v] = (int) order.size() - 1; | |
} | |
void do_dfs_from(int v) { | |
depth[v] = 0; | |
dist[v] = T{}; | |
root[v] = v; | |
pv[v] = pe[v] = -1; | |
do_dfs(v); | |
} | |
public: | |
void dfs(int v, bool clear_order = true) { | |
if (pv.empty()) { | |
init(); | |
} else { | |
if (clear_order) { | |
order.clear(); | |
} | |
} | |
do_dfs_from(v); | |
} | |
void dfs_all() { | |
init(); | |
for (int v = 0; v < n; v++) { | |
if (depth[v] == -1) { | |
do_dfs_from(v); | |
} | |
} | |
assert((int) order.size() == n); | |
} | |
}; | |
template <typename T> | |
class lca_forest : public dfs_forest<T> { | |
public: | |
using dfs_forest<T>::edges; | |
using dfs_forest<T>::g; | |
using dfs_forest<T>::n; | |
using dfs_forest<T>::pv; | |
using dfs_forest<T>::pos; | |
using dfs_forest<T>::end; | |
using dfs_forest<T>::depth; | |
int h; | |
vector<vector<int>> pr; | |
lca_forest(int _n) : dfs_forest<T>(_n) { | |
} | |
inline void build_lca() { | |
assert(!pv.empty()); | |
int max_depth = 0; | |
for (int i = 0; i < n; i++) { | |
max_depth = max(max_depth, depth[i]); | |
} | |
h = 1; | |
while ((1 << h) <= max_depth) { | |
h++; | |
} | |
pr.resize(n); | |
for (int i = 0; i < n; i++) { | |
pr[i].resize(h); | |
pr[i][0] = pv[i]; | |
} | |
for (int j = 1; j < h; j++) { | |
for (int i = 0; i < n; i++) { | |
pr[i][j] = (pr[i][j - 1] == -1 ? -1 : pr[pr[i][j - 1]][j - 1]); | |
} | |
} | |
} | |
inline bool anc(int x, int y) { | |
return (pos[x] <= pos[y] && end[y] <= end[x]); | |
} | |
inline int go_up(int x, int up) { | |
assert(!pr.empty()); | |
up = min(up, (1 << h) - 1); | |
for (int j = h - 1; j >= 0; j--) { | |
if (up & (1 << j)) { | |
x = pr[x][j]; | |
if (x == -1) { | |
break; | |
} | |
} | |
} | |
return x; | |
} | |
inline int lca(int x, int y) { | |
assert(!pr.empty()); | |
if (anc(x, y)) { | |
return x; | |
} | |
if (anc(y, x)) { | |
return y; | |
} | |
for (int j = h - 1; j >= 0; j--) { | |
if (pr[x][j] != -1 && !anc(pr[x][j], y)) { | |
x = pr[x][j]; | |
} | |
} | |
return pr[x][0]; | |
} | |
}; | |
template <typename T> | |
class hld_forest : public lca_forest<T> { | |
public: | |
using lca_forest<T>::edges; | |
using lca_forest<T>::g; | |
using lca_forest<T>::n; | |
using lca_forest<T>::pv; | |
using lca_forest<T>::sz; | |
using lca_forest<T>::pos; | |
using lca_forest<T>::order; | |
using lca_forest<T>::depth; | |
using lca_forest<T>::dfs; | |
using lca_forest<T>::dfs_all; | |
using lca_forest<T>::lca; | |
using lca_forest<T>::build_lca; | |
vector<int> head; | |
vector<int> visited; | |
hld_forest(int _n) : lca_forest<T>(_n) { | |
visited.resize(n); | |
} | |
void build_hld(const vector<int> &vs) { | |
for (int tries = 0; tries < 2; tries++) { | |
if (vs.empty()) { | |
dfs_all(); | |
} else { | |
order.clear(); | |
for (int v : vs) { | |
dfs(v, false); | |
} | |
assert((int) order.size() == n); | |
} | |
if (tries == 1) { | |
break; | |
} | |
for (int i = 0; i < n; i++) { | |
if (g[i].empty()) { | |
continue; | |
} | |
int best = -1, bid = 0; | |
for (int j = 0; j < (int) g[i].size(); j++) { | |
int id = g[i][j]; | |
int v = edges[id].from ^ edges[id].to ^ i; | |
if (pv[v] != i) { | |
continue; | |
} | |
if (sz[v] > best) { | |
best = sz[v]; | |
bid = j; | |
} | |
} | |
swap(g[i][0], g[i][bid]); | |
} | |
} | |
build_lca(); | |
head.resize(n); | |
for (int i = 0; i < n; i++) { | |
head[i] = i; | |
} | |
for (int i = 0; i < n - 1; i++) { | |
int x = order[i]; | |
int y = order[i + 1]; | |
if (pv[y] == x) { | |
head[y] = head[x]; | |
} | |
} | |
} | |
void build_hld(int v) { | |
build_hld(vector<int>(1, v)); | |
} | |
void build_hld_all() { | |
build_hld(vector<int>()); | |
} | |
bool apply_on_path(int x, int y, bool with_lca, function<void(int,int,bool)> f) { | |
// f(x, y, up): up -- whether this part of the path goes up | |
assert(!head.empty()); | |
int z = lca(x, y); | |
if (z == -1) { | |
return false; | |
} | |
{ | |
int v = x; | |
while (v != z) { | |
if (depth[head[v]] <= depth[z]) { | |
f(pos[z] + 1, pos[v], true); | |
break; | |
} | |
f(pos[head[v]], pos[v], true); | |
v = pv[head[v]]; | |
} | |
} | |
if (with_lca) { | |
f(pos[z], pos[z], false); | |
} | |
{ | |
int v = y; | |
int cnt_visited = 0; | |
while (v != z) { | |
if (depth[head[v]] <= depth[z]) { | |
f(pos[z] + 1, pos[v], false); | |
break; | |
} | |
visited[cnt_visited++] = v; | |
v = pv[head[v]]; | |
} | |
for (int at = cnt_visited - 1; at >= 0; at--) { | |
v = visited[at]; | |
f(pos[head[v]], pos[v], false); | |
} | |
} | |
return true; | |
} | |
}; | |
class segtree { | |
public: | |
struct node { | |
// don't forget to set default value (used for leaves) | |
// not necessarily neutral element! | |
int mn = 0; | |
int add = 0; | |
void apply(int l, int r, int v) { | |
mn += v; | |
add += v; | |
} | |
}; | |
node unite(const node &a, const node &b) const { | |
node res; | |
res.mn = min(a.mn, b.mn); | |
return res; | |
} | |
inline void push(int x, int l, int r) { | |
int y = (l + r) >> 1; | |
int z = x + ((y - l + 1) << 1); | |
if (tree[x].add != 0) { | |
tree[x + 1].apply(l, y, tree[x].add); | |
tree[z].apply(y + 1, r, tree[x].add); | |
tree[x].add = 0; | |
} | |
} | |
inline void pull(int x, int z) { | |
tree[x] = unite(tree[x + 1], tree[z]); | |
} | |
int n; | |
vector<node> tree; | |
void build(int x, int l, int r) { | |
if (l == r) { | |
return; | |
} | |
int y = (l + r) >> 1; | |
int z = x + ((y - l + 1) << 1); | |
build(x + 1, l, y); | |
build(z, y + 1, r); | |
pull(x, z); | |
} | |
template <typename M> | |
void build(int x, int l, int r, const vector<M> &v) { | |
if (l == r) { | |
tree[x].apply(l, r, v[l]); | |
return; | |
} | |
int y = (l + r) >> 1; | |
int z = x + ((y - l + 1) << 1); | |
build(x + 1, l, y, v); | |
build(z, y + 1, r, v); | |
pull(x, z); | |
} | |
node get(int x, int l, int r, int ll, int rr) { | |
if (ll <= l && r <= rr) { | |
return tree[x]; | |
} | |
int y = (l + r) >> 1; | |
int z = x + ((y - l + 1) << 1); | |
push(x, l, r); | |
node res{}; | |
if (rr <= y) { | |
res = get(x + 1, l, y, ll, rr); | |
} else { | |
if (ll > y) { | |
res = get(z, y + 1, r, ll, rr); | |
} else { | |
res = unite(get(x + 1, l, y, ll, rr), get(z, y + 1, r, ll, rr)); | |
} | |
} | |
pull(x, z); | |
return res; | |
} | |
template <typename... M> | |
void modify(int x, int l, int r, int ll, int rr, const M&... v) { | |
if (ll <= l && r <= rr) { | |
tree[x].apply(l, r, v...); | |
return; | |
} | |
int y = (l + r) >> 1; | |
int z = x + ((y - l + 1) << 1); | |
push(x, l, r); | |
if (ll <= y) { | |
modify(x + 1, l, y, ll, rr, v...); | |
} | |
if (rr > y) { | |
modify(z, y + 1, r, ll, rr, v...); | |
} | |
pull(x, z); | |
} | |
int find_first_knowingly(int x, int l, int r, const function<bool(const node&)> &f) { | |
if (l == r) { | |
return l; | |
} | |
push(x, l, r); | |
int y = (l + r) >> 1; | |
int z = x + ((y - l + 1) << 1); | |
int res; | |
if (f(tree[x + 1])) { | |
res = find_first_knowingly(x + 1, l, y, f); | |
} else { | |
res = find_first_knowingly(z, y + 1, r, f); | |
} | |
pull(x, z); | |
return res; | |
} | |
int find_first(int x, int l, int r, int ll, int rr, const function<bool(const node&)> &f) { | |
if (ll <= l && r <= rr) { | |
if (!f(tree[x])) { | |
return -1; | |
} | |
return find_first_knowingly(x, l, r, f); | |
} | |
push(x, l, r); | |
int y = (l + r) >> 1; | |
int z = x + ((y - l + 1) << 1); | |
int res = -1; | |
if (ll <= y) { | |
res = find_first(x + 1, l, y, ll, rr, f); | |
} | |
if (rr > y && res == -1) { | |
res = find_first(z, y + 1, r, ll, rr, f); | |
} | |
pull(x, z); | |
return res; | |
} | |
int find_last_knowingly(int x, int l, int r, const function<bool(const node&)> &f) { | |
if (l == r) { | |
return l; | |
} | |
push(x, l, r); | |
int y = (l + r) >> 1; | |
int z = x + ((y - l + 1) << 1); | |
int res; | |
if (f(tree[z])) { | |
res = find_last_knowingly(z, y + 1, r, f); | |
} else { | |
res = find_last_knowingly(x + 1, l, y, f); | |
} | |
pull(x, z); | |
return res; | |
} | |
int find_last(int x, int l, int r, int ll, int rr, const function<bool(const node&)> &f) { | |
if (ll <= l && r <= rr) { | |
if (!f(tree[x])) { | |
return -1; | |
} | |
return find_last_knowingly(x, l, r, f); | |
} | |
push(x, l, r); | |
int y = (l + r) >> 1; | |
int z = x + ((y - l + 1) << 1); | |
int res = -1; | |
if (rr > y) { | |
res = find_last(z, y + 1, r, ll, rr, f); | |
} | |
if (ll <= y && res == -1) { | |
res = find_last(x + 1, l, y, ll, rr, f); | |
} | |
pull(x, z); | |
return res; | |
} | |
segtree(int _n) : n(_n) { | |
assert(n > 0); | |
tree.resize(2 * n - 1); | |
build(0, 0, n - 1); | |
} | |
template <typename M> | |
segtree(const vector<M> &v) { | |
n = v.size(); | |
assert(n > 0); | |
tree.resize(2 * n - 1); | |
build(0, 0, n - 1, v); | |
} | |
node get(int ll, int rr) { | |
assert(0 <= ll && ll <= rr && rr <= n - 1); | |
return get(0, 0, n - 1, ll, rr); | |
} | |
node get(int p) { | |
assert(0 <= p && p <= n - 1); | |
return get(0, 0, n - 1, p, p); | |
} | |
template <typename... M> | |
void modify(int ll, int rr, const M&... v) { | |
assert(0 <= ll && ll <= rr && rr <= n - 1); | |
modify(0, 0, n - 1, ll, rr, v...); | |
} | |
// find_first and find_last call all FALSE elements | |
// to the left (right) of the sought position exactly once | |
int find_first(int ll, int rr, const function<bool(const node&)> &f) { | |
assert(0 <= ll && ll <= rr && rr <= n - 1); | |
return find_first(0, 0, n - 1, ll, rr, f); | |
} | |
int find_last(int ll, int rr, const function<bool(const node&)> &f) { | |
assert(0 <= ll && ll <= rr && rr <= n - 1); | |
return find_last(0, 0, n - 1, ll, rr, f); | |
} | |
}; | |
int main() { | |
ios::sync_with_stdio(false); | |
cin.tie(0); | |
int tt; | |
cin >> tt; | |
for (int qq = 1; qq <= tt; qq++) { | |
cout << "Case #" << qq << ": "; | |
int n; | |
cin >> n; | |
hld_forest<long long> g(n); | |
vector<int> aux(n); | |
for (int i = 0; i < n - 1; i++) { | |
int x, y, z, w; | |
cin >> x >> y >> z >> w; | |
--x; --y; | |
g.add(x, y, w); | |
aux[y] = z; | |
} | |
g.build_hld(0); | |
vector<int> order(n - 1); | |
iota(order.begin(), order.end(), 1); | |
sort(order.begin(), order.end(), [&](int i, int j) { | |
return g.dist[i] < g.dist[j]; | |
}); | |
segtree st(n); | |
for (int i = 1; i < n; i++) { | |
st.modify(g.pos[i], g.pos[i], aux[i]); | |
} | |
long long cnt = 0; | |
long long sum = 0; | |
for (int v : order) { | |
int mn = (int) 1e9; | |
g.apply_on_path(v, 0, false, [&](int from, int to, bool) { | |
mn = min(mn, st.get(from, to).mn); | |
}); | |
cnt += mn; | |
sum += (long long) mn * g.dist[v]; | |
g.apply_on_path(v, 0, false, [&](int from, int to, bool) { | |
st.modify(from, to, -mn); | |
}); | |
} | |
cout << cnt << " " << sum << '\n'; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment