图论乱做
杂
P4306 [JSOI2010] 连通数
SB 题。bitset 优化 floyd。\(O(\frac{n^3}{\omega})\)。你猜为什么我哇了很多发。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
using namespace std;
const int N = 2005;
int n, ans; bitset<N> f, con[N];
signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n; char c;
_for (i, 1, n) _for (j, 1, n) cin >> c, con[i][j] = (c - '0') | (i == j);
_for (k, 1, n) _for (i, 1, n) {
if (con[i][k]) f.set();
else f.reset(); con[i] |= f & con[k];
} _for (i, 1, n) ans += con[i].count(); cout << ans << "\n";
return 0;
}
最短路相关
AT_arc084_b [ABC077D] Small Multiple
同余最短路。考虑设 \(f_i\) 表示 \((x \equiv i) \bmod k\) 的所有 \(x\) 的数字之和 \(\min\)。
在一个数后面加一个数字相当于 $ \times 10 + x\(,\)i \to (i \times 10 + x) \bmod k$,边权为 \(x\)。
跑最短路即可。复杂度 \(O(k \log k)\),有 \(10\) 左右的常数。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define PII pair<int, int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
const int N = 1e6 + 5, INF = 1e9 + 5;
int k, S, dis[N], vis[N]; vector<PII > G[N];
struct Node {
int id, val;
inline bool operator < (const Node & t) const { return val > t.val; }
}; priority_queue<Node> q;
inline void chkmin(int & x, int y) { x = x < y ? x : y; }
inline void chkmax(int & x, int y) { x = x > y ? x : y; }
inline void Dijkstra(int S) {
_for (i, 0, N - 1) dis[i] = INF, vis[i] = 0; q.push((Node){S, dis[S] = 0});
while (q.size()) {
int u = q.top().id; q.pop();
if (vis[u]) continue; vis[u] = 1;
for (PII v : G[u]) if (dis[v.fi] > dis[u] + v.se) q.push((Node){v.fi, dis[v.fi] = dis[u] + v.se});
}
}
signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> k;
_for (i, 1, 9) G[S = k].pb(mp(i % k, i));
_for (i, 0, k - 1) _for (j, 0, 9) G[i].pb(mp((i * 10 + j) % k, j)); Dijkstra(S), cout << dis[0] << "\n";
return 0;
}
P3403 跳楼机
同余最短路。
首先将楼层数减一,变成从 \(0\) 开始计数。
设 \(f_i\) 表示能到达的楼层 \(\bmod a = i\) 的最低楼层数。答案即为 \(\sum_{i = 0}^{a - 1} (\lfloor \frac{h - f_i}{a} \rfloor + 1)\)。
复杂度 \(O(a \log a)\)。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define ll unsigned long long
#define PII pair<int, int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
const int N = 1e6 + 5; ll INF = 1ull << 63;
int a, b, c, S, vis[N]; vector<PII > G[N]; ll h, ans, dis[N];
struct Node {
int id; ll val;
inline bool operator < (const Node & t) const { return val > t.val; }
}; priority_queue<Node> q;
inline void Dijkstra(int S) {
_for (i, 0, N - 1) dis[i] = INF, vis[i] = 0; q.push((Node){S, dis[S] = 0ull});
while (q.size()) {
int u = q.top().id; q.pop();
if (vis[u]) continue; vis[u] = 1;
for (PII v : G[u]) if (dis[v.fi] > dis[u] + v.se) q.push((Node){v.fi, dis[v.fi] = dis[u] + v.se});
}
}
signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> h >> a >> b >> c, h -- ;
_for (i, 0, a - 1) G[i].pb(mp((i + b) % a, b)), G[i].pb(mp((i + c) % a, c)); Dijkstra(S = 0);
_for (i, 0, a - 1) if (dis[i] <= h) ans += (h - dis[i]) / a + 1; cout << ans << "\n";
return 0;
}
P2371 [国家集训队] 墨墨的等式
同上一到题。注意判 \(a_i = 0\) 的 corner case。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define _all(i, a, b) for (int i = (a); i >= (b); i -- )
#define ll unsigned long long
#define PII pair<int, int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
const int N = 1e6 + 5; ll INF = (ll)1e18 + 5;
int n, a[N], vis[N]; vector<PII > G[N]; ll l, r, dis[N];
struct Node {
int id; ll val;
inline bool operator < (const Node & t) const { return val > t.val; }
}; priority_queue<Node> q;
inline void Dijkstra(int S) {
_for (i, 0, N - 1) dis[i] = INF, vis[i] = 0; q.push((Node){S, dis[S] = 0ull});
while (q.size()) {
int u = q.top().id; q.pop();
if (vis[u]) continue; vis[u] = 1;
for (PII v : G[u]) if (dis[v.fi] > dis[u] + v.se) q.push((Node){v.fi, dis[v.fi] = dis[u] + v.se});
}
} inline ll gans(ll x) {
if ( ! x) return 0; ll res = 0;
_for (i, 0, a[1] - 1) if (dis[i] <= x) res += (x - dis[i]) / a[1] + 1; return res;
}
signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> l >> r;
_for (i, 1, n) cin >> a[i]; sort(a + 1, a + n + 1, [&] (int x, int y) { return x > y; });
while ( ! a[n]) n -- ;
if ( ! n) return cout << "0\n", 0;
_for (i, 0, a[1] - 1) _for (j, 2, n) G[i].pb(mp((i + a[j]) % a[1], a[j])); Dijkstra(0), cout << gans(r) - gans(l - 1) << "\n";
return 0;
}
P8060 [POI 2003] Sums
同余最短路,但不能直接建边(会 MLE),Dijkstra 的时候直接枚举出边即可。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define _all(i, a, b) for (int i = (a); i >= (b); i -- )
#define PII pair<int, int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
const int N = 5e4 + 5, INF = 1e9 + 5;
int n, Q, l, r, dis[N], a[N], vis[N];
struct Node {
int id, val;
inline bool operator < (const Node & t) const { return val > t.val; }
}; priority_queue<Node> q;
inline void Dijkstra(int S) {
_for (i, 0, N - 1) dis[i] = INF, vis[i] = 0; q.push((Node){S, dis[S] = 0ll});
while (q.size()) {
int u = q.top().id; q.pop();
if (vis[u]) continue; vis[u] = 1;
_for (i, 2, n) if (dis[(u + a[i]) % a[1]] > dis[u] + a[i]) q.push((Node){(u + a[i]) % a[1], dis[(u + a[i]) % a[1]] = dis[u] + a[i]});
}
} inline int gans(int x) {
if (x < 0ll) return 0; int res = 0;
_for (i, 0, a[1] - 1) if (dis[i] <= x) res += (x - dis[i]) / a[1] + 1; return res;
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n;
_for (i, 1, n) cin >> a[i]; cin >> Q, Dijkstra(0);
while (Q -- ) cin >> l, cout << (gans(l) - gans(l - 1) ? "TAK" : "NIE") << "\n";
return 0;
}
P7669 [JOI 2018 Final] 月票购买 / Commuter Pass / AT_joi2018ho_d 定期券 (Commuter Pass)
先在原图上求出 \(S \to T\) 的最短路。
考虑一个性质:最后答案的 \(U \to V\) 的最短路一定是形如 \(U \to x \to y \to V\),且 \(x \to y\) 是原图上最短路的一段。证明是简单的,如果有两段的话那中间那段一定不优。
于是建分层图,第一层和第三层是原图,第二层是边权置为 \(0\) 后的最短路边(注意要建 \(2\) 个版本,分别是正着的和反着的。因为如果在一起建图的话跑出来的路径不是原图的最短路了)。
复杂度 \(O(m \log m)\)。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define _all(i, a, b) for (int i = (a); i >= (b); i -- )
#define int long long
#define ll long long
#define PII pair<int, ll>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
const int N = 4e5 + 5; const ll INF = (ll)1e18 + 5;
int n, m, S, T, U, V; ll d;
struct Edge { int u, v, w; } e[N];
struct Graph {
int vis[N]; ll dis[N]; vector<PII > G[N];
struct Node {
int id; ll val;
inline bool operator < (const Node & t) const { return val > t.val; }
}; priority_queue<Node> q;
inline void _add(int u, int v, int w) { G[u].pb(mp(v, w)); }
inline void add_edge(int u, int v, int w) { _add(u, v, w), _add(v, u, w); }
inline void Dijkstra(int S) {
_for (i, 0, N - 1) dis[i] = INF, vis[i] = 0; q.push((Node){S, dis[S] = 0ll});
while (q.size()) {
int u = q.top().id; q.pop();
if (vis[u]) continue; vis[u] = 1;
for (PII v : G[u]) if (dis[v.fi] > dis[u] + v.se) q.push((Node){v.fi, dis[v.fi] = dis[u] + v.se});
}
}
} G[3];
signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m >> S >> T >> U >> V; int u, v, w;
_for (i, 1, m) cin >> u >> v >> w, G[0].add_edge(u, v, w), G[1].add_edge(u, v, w), e[i] = (Edge){u, v, w}, G[2].add_edge(u, v, w), G[2].add_edge(u + 3 * n, v + 3 * n, w); G[0].Dijkstra(S), G[1].Dijkstra(T), d = G[0].dis[T];
_for (i, 1, m) {
u = e[i].u, v = e[i].v, w = e[i].w;
if (G[0].dis[u] + G[1].dis[v] + w == d) G[2]._add(u + n, v + n, 0), G[2]._add(v + 2 * n, u + 2 * n, 0);
else if (G[0].dis[v] + G[1].dis[u] + w == d) G[2]._add(v + n, u + n, 0), G[2]._add(u + 2 * n, v + 2 * n, 0);
} _for (i, 1, n) G[2]._add(i, i + n, 0), G[2]._add(i, i + 2 * n, 0), G[2]._add(i + n, i + 3 * n, 0), G[2]._add(i + 2 * n, i + 3 * n, 0); G[2].Dijkstra(U), cout << G[2].dis[V + 3 * n] << "\n";
return 0;
}
点分治 / 点分树
P3806 【模板】点分治 1
把路径拆分成三种:不经过当前根 \(u\)、以当前根 \(u\) 为端点、穿过 \(u\)。
第一种可以递归子树求解。第三种可以拆分成两条第二种路径,所以只需统计第二种路径即可。
求出所有点到 \(u\) 的距离 \(dis\),然后做一遍背包状物看能不能拼凑两条路径使得和为 \(w\)。设树的大小为 \(sz\),复杂度是 \(O(sz \times q)\) 的。
点分治是每次选择当前子树的根作为分治中心,这样每次递归下去子树大小都会减半。每一层的复杂度为 \(O(q \sum sz) = O(nq)\),所以总复杂度是 \(O(n q \log n)\) 的。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define ll long long
#define PII pair<int, int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
const int N = 1e4 + 5, Q = 105, V = 1e7 + 5;
int n, q, top, rt, cnt, sz[N], val[N], dis[N], vis[N], qry[N]; vector<PII > Tr[N]; bitset<V> ans, ok;
inline void chkmax(int & x, int y) { x = x > y ? x : y; }
void get_sz(int u, int fa) {
sz[u] = 1, cnt ++ ;
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_sz(v.fi, u), sz[u] += sz[v.fi];
} void get_rt(int u, int fa) {
val[u] = cnt - sz[u];
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) chkmax(val[u], sz[v.fi]);
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_rt(v.fi, u);
if (val[u] < val[rt]) rt = u;
} void get_dis(int u, int fa, int d) {
dis[ ++ top] = d;
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_dis(v.fi, u, d + v.se);
} inline void solve(int u) {
top = cnt = 0, rt = u, get_sz(u, 0), get_rt(u, 0); int cur;
for (PII v : Tr[rt]) if ( ! vis[v.fi]) {
cur = top, get_dis(v.fi, rt, v.se);
_for (j, 1, q) if ( ! ans[j]) _for (i, cur + 1, top) {
if (dis[i] <= qry[j]) ans[j] = ans[j] | ok[qry[j] - dis[i]];
if (ans[j]) break;
} _for (i, cur + 1, top) if (dis[i] < V) ok[dis[i]] = 1;
} _for (i, 1, top) if (dis[i] < V) ok[dis[i]] = 0; vis[rt] = 1;
for (PII v : Tr[rt]) if ( ! vis[v.fi]) solve(v.fi);
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> q, ok[0] = 1; int u, v, w;
_for (i, 1, n - 1) cin >> u >> v >> w, Tr[u].pb(mp(v, w)), Tr[v].pb(mp(u, w));
_for (i, 1, q) cin >> qry[i]; solve(1);
_for (i, 1, q) cout << (ans[i] ? "AYE" : "NAY") << "\n";
return 0;
}
P4178 Tree
BIT 查询。复杂度 \(O(n \log n \log k)\)。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define ll long long
#define PII pair<int, int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
const int N = 4e4 + 5, V = 2e4 + 5;
int n, top, rt, cnt, k, sz[N], val[N], dis[N], vis[N], qry[N]; vector<PII > Tr[N]; ll ans;
inline void chkmax(int & x, int y) { x = x > y ? x : y; }
struct Fenwick_Tree {
int tr[N];
inline int lowbit(int x) { return (x & ( - x)); }
inline void update(int x, int k) { for (int i = x; i < V; i += lowbit(i)) tr[i] += k; }
inline int query(int x) { int res = 1; for (int i = x; i; i -= lowbit(i)) res += tr[i]; return res; }
inline int query(int l, int r) { return l <= r ? (l ? query(r) - query(l - 1) : query(r)) : 0; }
} BIT;
void get_sz(int u, int fa) {
sz[u] = 1, cnt ++ ;
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_sz(v.fi, u), sz[u] += sz[v.fi];
} void get_rt(int u, int fa) {
val[u] = cnt - sz[u];
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) chkmax(val[u], sz[v.fi]);
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_rt(v.fi, u);
if (val[u] < val[rt]) rt = u;
} void get_dis(int u, int fa, int d) {
dis[ ++ top] = d;
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_dis(v.fi, u, d + v.se);
} inline void solve(int u) {
top = cnt = 0, rt = u, get_sz(u, 0), get_rt(u, 0); int cur;
for (PII v : Tr[rt]) if ( ! vis[v.fi]) {
cur = top, get_dis(v.fi, rt, v.se);
_for (i, cur + 1, top) if (dis[i] <= k) ans += BIT.query(0, k - dis[i]);
_for (i, cur + 1, top) if (dis[i] <= k) BIT.update(dis[i], 1);
} _for (i, 1, top) if (dis[i] <= k) BIT.update(dis[i], - 1); vis[rt] = 1;
for (PII v : Tr[rt]) if ( ! vis[v.fi]) solve(v.fi);
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n; int u, v, w;
_for (i, 1, n - 1) cin >> u >> v >> w, Tr[u].pb(mp(v, w)), Tr[v].pb(mp(u, w)); cin >> k, solve(1), cout << ans << "\n";
return 0;
}
P4149 [IOI 2011] Race
板子。直接开桶 chkmin 即可。
复杂度 \(O(n \log n)\)。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define ll long long
#define PII pair<int, int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
const int N = 1e6 + 5;
int n, top, rt, cnt, k, ans, sz[N], f[N], dep[N], val[N], vis[N], qry[N]; vector<PII > Tr[N]; ll dis[N];
inline void chkmin(int & x, int y) { x = x < y ? x : y; }
inline void chkmax(int & x, int y) { x = x > y ? x : y; }
void get_sz(int u, int fa) {
sz[u] = 1, cnt ++ ;
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_sz(v.fi, u), sz[u] += sz[v.fi];
} void get_rt(int u, int fa) {
val[u] = cnt - sz[u];
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) chkmax(val[u], sz[v.fi]);
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_rt(v.fi, u);
if (val[u] < val[rt]) rt = u;
} void get_dis(int u, int fa, ll d, int depth) {
dis[ ++ top] = d, dep[top] = depth;
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_dis(v.fi, u, d + v.se, depth + 1);
} inline void solve(int u) {
top = cnt = 0, rt = u, get_sz(u, 0), get_rt(u, 0); int cur;
for (PII v : Tr[rt]) if ( ! vis[v.fi]) {
cur = top, get_dis(v.fi, rt, v.se, 1);
_for (i, cur + 1, top) if (dis[i] <= k) chkmin(ans, f[k - dis[i]] + dep[i]);
_for (i, cur + 1, top) if (dis[i] <= k) chkmin(f[dis[i]], dep[i]);
} _for (i, 1, top) if (dis[i] <= k) f[dis[i]] = N; vis[rt] = 1, f[0] = 0;
for (PII v : Tr[rt]) if ( ! vis[v.fi]) solve(v.fi);
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> k, ans = N; int u, v, w; fill(f + 1, f + N, N);
_for (i, 1, n - 1) cin >> u >> v >> w, u ++ , v ++ , Tr[u].pb(mp(v, w)), Tr[v].pb(mp(u, w)); solve(1), cout << (ans ^ N ? ans : - 1) << "\n";
return 0;
}
P2634 [国家集训队] 聪聪可可
较为简单。开大小为 \(3\) 的桶计数即可。复杂度 \(O(n \log n)\)。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define ll long long
#define PII pair<int, int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
const int N = 4e4 + 5, V = 2e4 + 5;
int n, top, rt, cnt, k, sz[N], f[3], val[N], vis[N]; vector<PII > Tr[N]; ll t, ans, dis[N];
inline void chkmax(int & x, int y) { x = x > y ? x : y; }
void get_sz(int u, int fa) {
sz[u] = 1, cnt ++ ;
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_sz(v.fi, u), sz[u] += sz[v.fi];
} void get_rt(int u, int fa) {
val[u] = cnt - sz[u];
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) chkmax(val[u], sz[v.fi]);
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_rt(v.fi, u);
if (val[u] < val[rt]) rt = u;
} void get_dis(int u, int fa, ll d) {
dis[ ++ top] = d;
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_dis(v.fi, u, d + v.se);
} inline void solve(int u) {
top = cnt = 0, rt = u, get_sz(u, 0), get_rt(u, 0); int cur;
for (PII v : Tr[rt]) if ( ! vis[v.fi]) {
cur = top, get_dis(v.fi, rt, v.se);
_for (i, cur + 1, top) ans += f[(3 - dis[i] % 3) % 3] << 1;
_for (i, cur + 1, top) f[dis[i] % 3] ++ ;
} _for (i, 1, top) f[dis[i] % 3] -- ; vis[rt] = 1;
for (PII v : Tr[rt]) if ( ! vis[v.fi]) solve(v.fi);
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n, f[0] = 1, ans = n; int u, v, w;
_for (i, 1, n - 1) cin >> u >> v >> w, Tr[u].pb(mp(v, w)), Tr[v].pb(mp(u, w)); solve(1), t = __gcd(ans, 1ll * n * n), cout << ans / t << "/" << 1ll * n * n / t << "\n";
return 0;
}
P2664 树上游戏
贡献不好算,考虑:设 \(cnt_j\) 表示颜色为 \(j\) 且一个端点为 \(u\) 的路径条数。则 \(ans_u = \sum_{j}\)。
考虑点分治,贡献分为:以当前根 \(rt\) 为端点、以 \(rt\) 为 LCA。
前者好统计:dfs 这棵树,遇到一个新颜色将答案加上当前点 \(sz\),意义显然;
后者稍微复杂一点,要分为两类贡献(对 \(rt\) 子树内的点 \(u\) 答案的贡献):
-
在 \(u \to rt\) 路径上的颜色:对于这种颜色,贡献为 \(sz_{rt} - sz_u\)。
-
不在 \(u \to rt\) 路径上的颜色:先统计全局的颜色信息,然后往下 dfs,遇到一个新颜色就将该颜色的贡献减去即可。
写出来会比较史。复杂度是 \(O(n \log n)\) 的。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define ll long long
#define pb push_back
using namespace std;
const int N = 4e5 + 5;
int n, cnt, rt, op, a[N], col[N], vis[N], val[N], sz[N], f[N]; vector<int> Tr[N]; ll s, ans[N];
inline void chkmax(int & x, int y) { x = x > y ? x : y; }
void get_sz(int u, int fa) {
sz[u] = 1;
if (op) cnt ++ ;
for (int v : Tr[u]) if (v ^ fa && ! vis[v]) get_sz(v, u), sz[u] += sz[v];
} void get_rt(int u, int fa) {
val[u] = cnt - sz[u];
for (int v : Tr[u]) if (v ^ fa && ! vis[v]) chkmax(val[u], sz[v]);
for (int v : Tr[u]) if (v ^ fa && ! vis[v]) get_rt(v, u);
if (val[u] < val[rt]) rt = u;
} void dfs1(int u, int fa, int rt) {
if ( ! col[a[u]]) ans[rt] += sz[u], s += sz[u], f[a[u]] += sz[u]; col[a[u]] ++ ;
for (int v : Tr[u]) if (v ^ fa && ! vis[v]) dfs1(v, u, rt);
col[a[u]] -- ;
} void dfs2(int u, int fa, int p, int x) {
if ( ! col[a[u]]) p ++ ; col[a[u]] ++ , ans[u] += 1ll * p * x;
for (int v : Tr[u]) if (v ^ fa && ! vis[v]) dfs2(v, u, p, x);
col[a[u]] -- ;
} void dfs3(int u, int fa, int k) {
if ( ! col[a[u]]) s += k * sz[u], f[a[u]] += k * sz[u]; col[a[u]] ++ ;
for (int v : Tr[u]) if (v ^ fa && ! vis[v]) dfs3(v, u, k);
col[a[u]] -- ;
} void dfs4(int u, int fa, ll k) {
if ( ! col[a[u]]) k -= f[a[u]]; col[a[u]] ++ , ans[u] += k;
for (int v : Tr[u]) if (v ^ fa && ! vis[v]) dfs4(v, u, k);
col[a[u]] -- ;
} void clear(int u, int fa) {
col[a[u]] = f[a[u]] = sz[u] = 0;
for (int v : Tr[u]) if (v ^ fa && ! vis[v]) clear(v, u);
} inline void solve(int u) {
cnt = 0, rt = u, op = 1, get_sz(u, 0), get_rt(u, 0), s = op = 0, col[a[rt]] ++ , ans[rt] += cnt, s += cnt, f[a[rt]] += cnt;
for (int v : Tr[rt]) if ( ! vis[v]) get_sz(v, rt), dfs1(v, rt, rt), dfs2(v, rt, 1, cnt - sz[v]);
for (int v : Tr[rt]) if ( ! vis[v]) s -= cnt, f[a[rt]] -= cnt, col[a[rt]] ++ , dfs3(v, rt, - 1), dfs4(v, rt, s), dfs3(v, rt, 1), s += cnt, f[a[rt]] += cnt, col[a[rt]] -- ; clear(rt, 0), vis[rt] = 1;
for (int v : Tr[rt]) if ( ! vis[v]) solve(v);
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n; int u, v;
_for (i, 1, n) cin >> a[i];
_for (i, 2, n) cin >> u >> v, Tr[u].pb(v), Tr[v].pb(u); solve(1);
_for (i, 1, n) cout << ans[i] << "\n";
return 0;
}
P3714 [BJOI2017] 树的难题
考虑点分治。考虑路径的合并存在两种情况:中间连接处的颜色是否相同。
遍历子树后将所有点按照最后一条边的颜色大小排序,以保证相同的颜色的边在一起。开两棵线段树分别统计两者,每遇到一个不同的颜色。就将第二棵树上的值传给第一棵树并清空。
复杂度 \(O(n \log^2 n)\)。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define ll long long
#define PII pair<int, int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
const int N = 2e5 + 5; const ll INF = (ll)1e18 + 5;
int n, m, L, R, top, rt, cnt, a[N], sz[N], val[N], vis[N]; ll ans = - INF; vector<PII > Tr[N];
inline void chkmin(int & x, int y) { x = x < y ? x : y; }
inline void chkmax(ll & x, ll y) { x = x > y ? x : y; }
struct Node { int x, y, len; ll z; } w[N];
struct Segment_Tree {
#define lc (p << 1)
#define rc (p << 1 | 1)
#define mid ((tr[p].l + tr[p].r) >> 1)
struct Tree { int l, r; ll val; } tr[N << 2];
inline int len(int p) { return tr[p].r - tr[p].l + 1; }
inline bool In(int p, int l, int r) { return l <= tr[p].l && tr[p].r <= r; }
inline void push_up(int p) { tr[p].val = max(tr[lc].val, tr[rc].val); }
void build(int p, int l, int r) {
tr[p].l = l, tr[p].r = r, tr[p].val = - INF;
if (l ^ r) build(lc, l, mid), build(rc, mid + 1, r);
} void update(int p, int x, int k) { if (len(p) == 1) return chkmax(tr[p].val, k); update(x <= mid ? lc : rc, x, k); push_up(p); }
void clear(int p, int x) { if (tr[p].val == - INF) return ; if (len(p) == 1) return tr[p].val = - INF, void(); clear(x <= mid ? lc : rc, x), push_up(p); }
ll query(int p, int l, int r) {
if (l > r) return - INF;
if (In(p, l, r)) return tr[p].val; ll res = - INF;
if (l <= mid) chkmax(res, query(lc, l, r));
if (r > mid) chkmax(res, query(rc, l, r)); return res;
}
#undef lc
#undef rc
#undef mid
} SGT1, SGT2;
void get_sz(int u, int fa) {
sz[u] = 1, cnt ++ ;
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_sz(v.fi, u), sz[u] += sz[v.fi];
} void get_rt(int u, int fa) {
val[u] = cnt - sz[u];
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) val[u] = max(val[u], sz[v.fi]);
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_rt(v.fi, u);
if (val[u] < val[rt]) rt = u;
} void get_w(int u, int fa, ll cur, int lst, int fir, int rt, int len) {
w[ ++ top] = (Node){fir, rt, len, cur};
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_w(v.fi, u, cur + (v.se != lst) * a[v.se], v.se, fir, rt, len + 1);
} inline void solve(int u) {
top = cnt = 0, rt = u, get_sz(u, 0), get_rt(u, 0); int cur; SGT1.update(1, 0, 0);
for (PII v : Tr[rt]) if ( ! vis[v.fi]) get_w(v.fi, rt, a[v.se], v.se, v.se, v.fi, 1); sort(w + 1, w + top + 1, [&] (Node p, Node q) { return p.x ^ q.x ? p.x < q.x : p.y < q.y; });
_for (i, 1, top) {
if (w[i].len <= R) chkmax(ans, max(SGT1.query(1, max(0, L - w[i].len), R - w[i].len) + w[i].z, SGT2.query(1, max(0, L - w[i].len), R - w[i].len) + w[i].z - a[w[i].x]));
if (w[i].y ^ w[i + 1].y) {
cur = i;
while (w[cur].y == w[i].y) { if (w[cur].len <= R) SGT2.update(1, w[cur].len, w[cur].z); cur -- ; }
} if (w[i].x ^ w[i + 1].x) {
cur = i;
while (w[cur].x == w[i].x) { if (w[cur].len <= R) SGT1.update(1, w[cur].len, w[cur].z), SGT2.clear(1, w[cur].len); cur -- ; }
}
} _for (i, 1, top) if (w[i].len <= R) SGT1.clear(1, w[i].len), SGT2.clear(1, w[i].len); vis[rt] = 1;
for (PII v : Tr[rt]) if ( ! vis[v.fi]) solve(v.fi);
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m >> L >> R; int u, v, w; SGT1.build(1, 0, n), SGT2.build(1, 0, n);
_for (i, 1, m) cin >> a[i];
_for (i, 2, n) cin >> u >> v >> w, Tr[u].pb(mp(v, w)), Tr[v].pb(mp(u, w)); solve(1), cout << ans << "\n";
return 0;
}
P5306 [COCI 2018/2019 #5] Transport
仍然考虑点分治。我们在 dfs 时分别记录当前的油量 \(cur_u\) 以及 历史上最小的油量 \(his\)(要分走的方向 dfs,\(his\) 的求法是不同的)。
首先如果 \(his_u \ge 0\),则代表 \(u\) 可以走到 \(rt\) 或者 \(rt\) 可以走到 \(u\),直接计数即可。
剩下的情况是先 \(u \to rt\) 再 \(rt \to v\)。那么我们需要满足 \(cur_u + his_v \ge 0\),\(u\) 才能够走到 \(v\)(其中 \(cur_u\) 表示 \(u\) 走到 \(rt\) 后的油量,\(his_v\) 表示 \(rt\) 走到 \(v\) 时整个过程中的最小油量)。这个式子的意义是显然的。
按照权值 sort 之后直接上双指针即可。注意要减去同一棵子树内的情况。复杂度 \(O(n \log^2 n)\),瓶颈在于 sort。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define _all(i, a, b) for (int i = (a); i >= (b); i -- )
#define ll long long
#define PII pair<int, int>
#define PLI pair<ll, int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
const int N = 2e5 + 5; const ll INF = (ll)1e18 + 5;
int n, top1, top2, rt, cnt, op, occ[N], a[N], sz[N], val[N], vis[N]; ll ans; vector<PII > Tr[N]; PLI num1[N], num2[N];
void get_sz(int u, int fa) {
sz[u] = 1, cnt ++ ;
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_sz(v.fi, u), sz[u] += sz[v.fi];
} void get_rt(int u, int fa) {
val[u] = cnt - sz[u];
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) val[u] = max(val[u], sz[v.fi]);
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_rt(v.fi, u);
if (val[u] < val[rt]) rt = u;
} void dfs1(int u, int fa, ll his, ll cur, int rt) {
if ( ! op || (op && his >= 0)) num1[ ++ top1] = mp(op ? cur : his, rt);
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) dfs1(v.fi, u, min(0ll, his) + a[v.fi] - v.se, cur + a[v.fi] - v.se, rt);
} void dfs2(int u, int fa, ll his, ll cur, int rt) {
num2[ ++ top2] = mp(his, rt);
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) dfs2(v.fi, u, min(his, cur + a[u] - v.se), cur + a[u] - v.se, rt);
}
inline void solve(int u) {
top1 = top2 = cnt = 0, rt = u, get_sz(u, 0), get_rt(u, 0), op = 0;
for (PII v : Tr[rt]) if ( ! vis[v.fi]) dfs1(v.fi, rt, a[v.fi] - v.se, a[v.fi] - v.se, v.fi), dfs2(v.fi, rt, a[rt] - v.se, a[rt] - v.se, v.fi);
_for (i, 1, top1) ans += num1[i].fi >= 0;
_for (i, 1, top2) ans += num2[i].fi >= 0; top1 = 0, op = 1;
for (PII v : Tr[rt]) if ( ! vis[v.fi]) dfs1(v.fi, rt, a[v.fi] - v.se, a[v.fi] - v.se, v.fi); int p = top2 + 1; sort(num1 + 1, num1 + top1 + 1), sort(num2 + 1, num2 + top2 + 1);
_for (i, 1, top1) { while (p > 1 && num1[i].fi + num2[p - 1].fi >= 0) occ[num2[ -- p].se] ++ ; ans += top2 - p + 1 - occ[num1[i].se]; }
_for (i, 1, top2) occ[num2[i].se] = 0; vis[rt] = 1;
for (PII v : Tr[rt]) if ( ! vis[v.fi]) solve(v.fi);
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n; int u, v, w;
_for (i, 1, n) cin >> a[i];
_for (i, 2, n) cin >> u >> v >> w, Tr[u].pb(mp(v, w)), Tr[v].pb(mp(u, w)); solve(1), cout << ans << "\n";
return 0;
}
P4886 快递员
假设我们现在的最优根是 \(u\),答案为 \(k\)。观察一下性质:
-
如果有一条路线经过 \(u\),则答案一定是 \(k\)(显然);
-
如果有两条路径分别在 \(u\) 的两棵子树里,则答案一定是 \(k\)(动一下的话左右一定会有一个变大);
所以如果想要得到更优答案,所有路径一定在 \(u\) 的同一棵子树里。所以我们可以递归该子树求解。
单次做是 \(O(n + q)\) 的。为了保证复杂度,上点分治,只需递归 \(\log n\) 层,总复杂度 \(O((n + q) \log n)\)。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define ll long long
#define PII pair<int, int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
const int N = 4e5 + 5; const ll INF = (ll)1e18 + 5;
int n, q, rt, cnt, tp, lsttot = N << 2, cur_rt, tot[N], rts[N], sz[N], x[N], y[N], vis[N], w[N]; vector<PII > Tr[N]; ll ans = INF, dis[N];
inline void chkmin(ll & x, ll y) { x = x < y ? x : y; }
inline void chkmax(ll & x, ll y) { x = x > y ? x : y; }
void get_sz(int u, int fa) {
sz[u] = 1, cnt ++ ;
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_sz(v.fi, u), sz[u] += sz[v.fi];
} void get_rt(int u, int fa) {
w[u] = cnt - sz[u];
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) w[u] = max(w[u], sz[v.fi]);
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_rt(v.fi, u);
if (w[u] < w[rt]) rt = u;
} void dfs1(int u, int fa) {
rts[u] = tp;
for (PII v : Tr[u]) if (v.fi ^ fa) dfs1(v.fi, u);
}
void get_dis(int u, int fa) { for (PII v : Tr[u]) if (v.fi ^ fa) dis[v.fi] = dis[u] + v.se, get_dis(v.fi, u); }
void solve(int u) {
if (vis[u] || ! u) return ;
_for (i, 1, n) dis[i] = - INF, tot[i] = rts[i] = sz[i] = 0; tp = N - 1, dis[u] = cnt = 0, cur_rt = u, get_sz(u, 0), get_dis(u, 0); ll t, cur = 0; vector<int> vec;
for (PII v : Tr[u]) tp = v.fi, dfs1(v.fi, u);
_for (i, 1, q) {
if ((t = dis[x[i]] + dis[y[i]]) > cur) cur = t, vec.clear();
if (t == cur) vec.pb(i);
} int Ray_Wu = 0; chkmin(ans, cur);
for (int i : vec) {
if (rts[x[i]] ^ rts[y[i]]) return ; tot[rts[x[i]]] ++ ;
if (tot[rts[x[i]]] ^ ( ++ Ray_Wu)) return ;
} if (vec.size()) rt = cnt = 0, vis[u] = 1, get_sz(rts[x[vec[0]]], 0), get_rt(rts[x[vec[0]]], 0), solve(rt);
}
signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> q, w[0] = N; int u, v, w;
_for (i, 2, n) cin >> u >> v >> w, Tr[u].pb(mp(v, w)), Tr[v].pb(mp(u, w));
_for (i, 1, q) cin >> x[i] >> y[i]; get_sz(1, 0), get_rt(1, 0), solve(rt), cout << ans << "\n";
return 0;
}
P4292 [WC2010] 重建计划
积累一个 trick:要求路径的平均值最大,考虑二分答案 \(x\),判断 \(\frac{\sum_{E} val}{|E|} \ge x\) 即可。
把式子转化一下,将每条边的边权减去 \(x\),然后判断 \(x\) 是否合法就变为了:是否存在长度在 \([L, R]\) 之间,且权值和 \(\ge 0\) 的路径。
上点分治,如果线段树维护最大值,复杂度是带大常数的 \(O(n \log^3 n)\),是过不去的。
$O(n \log^3 n)$ Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define _all(i, a, b) for (int i = (a); i >= (b); i -- )
#define ll long long
#define ld long double
#define PII pair<int, ld>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
const int N = 1e5 + 5; const ld eps = 1e-4, INF = 1e15;
int n, L, R, rt, root, cnt, res, top, vis[N], sz[N], w[N]; vector<PII > Tr[N]; PII f[N]; vector<int> upd, T[N];
inline void chkmax(int & x, int y) { x = x > y ? x : y; }
inline int cmp(ld x, ld y) { return fabs(x - y) <= eps ? 0 : (x > y ? 1 : - 1); }
struct Segment_Tree {
#define lc (p << 1)
#define rc (p << 1 | 1)
#define mid ((tr[p].l + tr[p].r) >> 1)
struct Tree { int l, r; ld val; } tr[N << 2];
inline int len(int p) { return tr[p].r - tr[p].l + 1; }
inline bool In(int p, int l, int r) { return l <= tr[p].l && tr[p].r <= r; }
inline void push_up(int p) { tr[p].val = fmax(tr[lc].val, tr[rc].val); }
void build(int p, int l, int r) {
tr[p].l = l, tr[p].r = r, tr[p].val = - INF;
if (l == r) return ; build(lc, l, mid), build(rc, mid + 1, r);
} void update(int p, int x, ld k) { if (len(p) == 1) return tr[p].val = fmax(tr[p].val, k), void(); update(x <= mid ? lc : rc, x, k), push_up(p); }
void clear(int p, int x) { if ( ! cmp(tr[p].val, - INF)) return ; if (len(p) == 1) return tr[p].val = - INF, void(); clear(x <= mid ? lc : rc, x), push_up(p); }
ld query(int p, int l, int r) {
if (In(p, l, r)) return tr[p].val;
if (r <= mid) return query(lc, l, r);
if (l > mid) return query(rc, l, r); return fmax(query(lc, l, r), query(rc, l, r));
}
#undef lc
#undef rc
#undef mid
} SGT;
int fuck = 0;
void get_sz(int u, int fa) {
sz[u] = 1, cnt ++ ;
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_sz(v.fi, u), sz[u] += sz[v.fi];
} void get_rt(int u, int fa) {
w[u] = cnt - sz[u];
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) chkmax(w[u], sz[v.fi]);
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_rt(v.fi, u);
if (w[u] < w[rt]) rt = u;
} void get_dist(int u, int fa, int dep, ld d) {
if ((L <= dep && dep <= R && cmp(d, 0) >= 0) || res) return res = 1, void(); f[ ++ top] = mp(dep, d);
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_dist(v.fi, u, dep + 1, d + v.se);
} void build(int u, int fa) {
rt = u, cnt = 0, get_sz(u, 0), get_rt(u, 0), vis[rt] = 1;
if (fa) T[fa].pb(rt), fuck ++ ;
else root = rt;
assert(fuck <= n - 1);
for (PII v : Tr[rt]) if ( ! vis[v.fi]) build(v.fi, rt); vis[u] = 0;
} void solve(int rt) {
for (PII v : Tr[rt]) if ( ! vis[v.fi] && ! res) {
top = 0, get_dist(v.fi, rt, 1, v.se);
if ( ! res) _for (i, 1, top) if ( ! res && f[i].fi <= R && cmp(SGT.query(1, max(0, L - f[i].fi), R - f[i].fi) + f[i].se, 0) >= 0) res = 1;
if ( ! res) _for (i, 1, top) if ( ! res && f[i].fi <= R) SGT.update(1, f[i].fi, f[i].se), upd.pb(f[i].fi);
} for (int i : upd) SGT.clear(1, i); upd.clear(), vis[rt] = 1;
for (int v : T[rt]) solve(v);
} inline bool valid(ld x) {
_for (u, 1, n) vis[u] = sz[u] = w[u] = 0;
_for (u, 1, n) _for (i, 0, (int)Tr[u].size() - 1) Tr[u][i].se -= x; res = 0, solve(root);
_for (u, 1, n) _for (i, 0, (int)Tr[u].size() - 1) Tr[u][i].se += x; return res;
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> L >> R; int u, v, w; ld l = 0, r = 1000000, mid, ans = 0; SGT.build(1, 0, n);
_for (i, 2, n) cin >> u >> v >> w, Tr[u].pb(mp(v, w)), Tr[v].pb(mp(u, w)); build(1, 0);
assert(fuck == n - 1);
while (cmp(l, r)) mid = (l + r) / 2.0, valid(mid) ? (ans = mid, l = mid) : r = mid; cout << fixed << setprecision(3) << ans << "\n";
return 0;
}
然后你考虑如果你一段的长度减小,那么能与之匹配的长度区间是递增的。
于是可以直接上单调队列维护即可。复杂度 \(O(n \log^2 n)\)。
为了保证复杂度需要将子树按照 \(maxdep\) 排序。
$O(n \log^2 n)$ Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define _all(i, a, b) for (int i = (a); i >= (b); i -- )
#define ll long long
#define ld long double
#define PII pair<int, ld>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
const int N = 1e5 + 5; const ld eps = 1e-4, INF = 1e15;
int n, L, R, rt, cnt, res, top, q[N], vis[N], sz[N], w[N]; vector<PII > Tr[N]; ld val[N];
inline void chkmax(int & x, int y) { x = x > y ? x : y; }
inline int cmp(ld x, ld y) { return fabs(x - y) <= eps ? 0 : (x > y ? 1 : - 1); }
struct Node {
int maxdep; vector<PII > vec;
inline void ins(PII x) { chkmax(maxdep, x.fi), vec.pb(x); }
inline void st() { sort(vec.begin(), vec.end(), [&] (PII x, PII y) { return x.fi > y.fi; }); }
} ; vector<Node> a[N];
void get_sz(int u, int fa) {
sz[u] = 1, cnt ++ ;
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_sz(v.fi, u), sz[u] += sz[v.fi];
} void get_rt(int u, int fa) {
w[u] = cnt - sz[u];
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) chkmax(w[u], sz[v.fi]);
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_rt(v.fi, u);
if (w[u] < w[rt]) rt = u;
} void get_dist(int u, int fa, int dep, ld dis, Node & it) {
it.ins(mp(dep, dis));
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_dist(v.fi, u, dep + 1, dis + v.se, it);
} void build(int u) {
rt = u, cnt = 0, get_sz(u, 0), get_rt(u, 0), vis[rt] = 1;
for (PII v : Tr[rt]) if ( ! vis[v.fi]) a[rt].pb(Node()), get_dist(v.fi, rt, 1, v.se, a[rt].back()), a[rt].back().st(); sort(a[rt].begin(), a[rt].end(), [&] (Node x, Node y) { return x.maxdep < y.maxdep; });
for (PII v : Tr[rt]) if ( ! vis[v.fi]) build(v.fi);
} inline bool valid(ld x) {
int res = 0, pos, lb, rb, hd, tl, cur;
_for (i, 0, n) val[i] = - INF;
_for (i, 1, n) {
cur = 0;
for (Node j : a[i]) {
hd = 1, tl = pos = 0;
for (PII k : j.vec) {
lb = max(0, L - k.fi), rb = min(cur, R - k.fi);
if (lb > rb) continue;
_for (i, pos + 1, rb) { while (hd <= tl && val[q[tl]] < val[i]) tl -- ; q[ ++ tl] = i; }
while (hd <= tl && q[hd] < lb) hd ++ ; pos = rb;
if (hd <= tl && cmp(val[q[hd]] + k.se - x * k.fi, 0) >= 0) { res = 1; goto here; }
} for (PII k : j.vec) {
val[k.fi] = fmax(val[k.fi], k.se - x * k.fi);
if (L <= k.fi && k.fi <= R && cmp(val[k.fi], 0) >= 0) { res = 1; goto here; }
} cur = j.maxdep;
} here : ;
for (Node j : a[i]) for (PII k : j.vec) val[k.fi] = - INF;
if (res) return 1;
} return 0;
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> L >> R; int u, v, w; ld l = 0, r = 1000000, mid, ans = 0;
_for (i, 2, n) cin >> u >> v >> w, Tr[u].pb(mp(v, w)), Tr[v].pb(mp(u, w)); build(1);
while (cmp(l, r)) mid = (l + r) / 2.0, valid(mid) ? (ans = mid, l = mid) : r = mid; cout << fixed << setprecision(3) << ans << "\n";
return 0;
}
P9260 [PA 2022] Miny
炸弹 的树上版本。
仍然考虑优化建图。与树上距离有关,容易想到点分治。
设我们当前的分治中心为 \(rt\),若 \(dis_{rt, u} + dis_{rt, v} \le r_u\),则 \(u\) 可以引爆 \(v\)。
转化一下得 \(dis_{rt, v} \le r_u - dis_{rt, u}\),对于每个 \(rt\) 和 \(u\),满足条件的 \(v\) 一定是 \(rt\) 子树内 \(dis_{rt, v}\) 较小的一段前缀。
于是直接前缀和优化即可。
那么本题问什么不需要减去同一子树的贡献呢?因为不会影响答案(思考一下发现是显然的)。
复杂度 \(O(n \log^2 n)\),瓶颈在于建图。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define _all(i, a, b) for (int i = (a); i >= (b); i -- )
#define ll long long
#define ld long double
#define VI vector<int>
#define PII pair<int, ll>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
const int N = 1e5 + 4, M = N * 80;
int n, cnt, rt, scc, dfn_cnt, ncnt, ecnt, tp, id_cnt, root[M], g[M], sz[N], ID[M], fc[M], son[N], dep[N], top[N], fa[N], st[M], ed[M], low[M], dfn[M], in_deg[M], in_stack[M], stk[M], bel[M], up[N], w[M], vis[N]; ll a[N], dis[N]; vector<PII > Tr[N], val[N]; set<int> G2[M]; vector<int> G[M], id[N]; queue<int> q;
inline void chkmin(int & x, int y) { x = x < y ? x : y; }
inline void chkmax(int & x, int y) { x = x > y ? x : y; }
struct Segment_Tree {
#define lc(p) tr[p].ls
#define rc(p) tr[p].rs
#define mid ((l + r) >> 1)
int ncnt = 0; struct Tree { int ls, rs, val; } tr[N * 600];
inline int clone(int p) { return tr[ ++ ncnt] = tr[p], ncnt; }
void update(int & p, int l, int r, int x, int k) {
if ( ! p) p = clone(p); tr[p].val += k;
if (l ^ r) x <= mid ? update(lc(p), l, mid, x, k) : update(rc(p), mid + 1, r, x, k);
} void merge(int & u, int v, int l, int r) {
if ( ! u || ! v || u == v) return u |= v, void();
if (tr[u].val == fc[r] - fc[l - 1]) return ;
if (tr[v].val == fc[r] - fc[l - 1]) return u = v, void(); u = clone(u), merge(lc(u), lc(v), l, mid), merge(rc(u), rc(v), mid + 1, r), tr[u].val = tr[lc(u)].val + tr[rc(u)].val;
}
#undef lc
#undef rc
#undef mid
} SGT;
inline void add_edge(int u, int v) { G[u].pb(v), ecnt ++ , st[ecnt] = u, ed[ecnt] = v; }
void dfs1(int u) {
sz[u] = 1, son[u] = - 1;
for (PII v : Tr[u]) if (v.fi ^ fa[u]) {
fa[v.fi] = u, dep[v.fi] = dep[u] + 1, dis[v.fi] = dis[u] + v.se, dfs1(v.fi), sz[u] += sz[v.fi];
if ( ! ~ son[u] || sz[son[u]] < sz[v.fi]) son[u] = v.fi;
}
} void dfs2(int u, int Top) {
top[u] = Top;
if ( ~ son[u]) dfs2(son[u], Top);
for (PII v : Tr[u]) if (v.fi ^ son[u] && v.fi ^ fa[u]) dfs2(v.fi, v.fi);
} inline int lca(int u, int v) {
int U = top[u], V = top[v];
while (U ^ V) {
if (dep[U] >= dep[V]) u = fa[U];
else v = fa[V]; U = top[u], V = top[v];
} return (dep[u] < dep[v] ? u : v);
} inline ll dist(int u, int v) { return dis[u] + dis[v] - 2ll * dis[lca(u, v)]; }
void get_sz(int u, int fa) {
sz[u] = 1, cnt ++ ;
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_sz(v.fi, u), sz[u] += sz[v.fi];
} void get_rt(int u, int fa) {
w[u] = cnt - sz[u];
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) chkmax(w[u], sz[v.fi]);
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_rt(v.fi, u);
if (w[u] < w[rt]) rt = u;
} void get_dist(int u, int fa, ll dist) {
val[rt].pb(mp(u, dist));
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_dist(v.fi, u, dist + v.se);
} void solve(int u, int lst) {
rt = u, cnt = 0, get_sz(u, 0), get_rt(u, 0), get_dist(rt, 0, 0), sort(val[rt].begin(), val[rt].end(), [&] (PII x, PII y) { return x.se ^ y.se ? x.se < y.se : x.fi < y.fi; }), vis[rt] = 1; int tmp = rt; up[rt] = lst, id[rt].resize(val[rt].size());
_for (i, 0, (int)val[rt].size() - 1) add_edge(id[rt][i] = ++ ncnt, val[rt][i].fi);
_for (i, 1, (int)val[rt].size() - 1) add_edge(id[rt][i], id[rt][i - 1]);
for (PII v : Tr[rt]) if ( ! vis[v.fi]) solve(v.fi, tmp);
} inline void upd(int u) {
int l, r, mid, pos; ll x;
for (int i = u; i; i = up[i]) {
x = dist(i, u), l = 0, r = val[i].size() - 1, pos = - 1;
while (l <= r) mid = (l + r) >> 1, val[i][mid].se + x <= a[u] ? (pos = mid, l = mid + 1) : r = mid - 1;
if ( ~ pos) add_edge(u, id[i][pos]);
}
} void Tarjan(int u) {
dfn[u] = low[u] = ++ dfn_cnt, stk[ ++ tp] = u, in_stack[u] = 1;
for (int v : G[u]) {
if ( ! dfn[v]) Tarjan(v), chkmin(low[u], low[v]);
else if (in_stack[v]) chkmin(low[u], dfn[v]);
} if (low[u] == dfn[u]) { scc ++ ; do { bel[stk[tp]] = scc, in_stack[stk[tp]] = 0; } while (stk[tp -- ] ^ u); }
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n, ncnt = n; int u, v; ll W;
_for (i, 1, n) cin >> a[i];
_for (i, 2, n) cin >> u >> v >> W, Tr[u].pb(mp(v, W)), Tr[v].pb(mp(u, W)); dfs1(1), dfs2(1, 1), solve(1, 0);
_for (i, 1, n) upd(i), dfn[i] = 0; dfn_cnt = 0;
_for (i, 1, ncnt) if ( ! dfn[i]) Tarjan(i);
_for (i, 1, n) g[bel[i]] ++ ;
_for (i, 1, scc) if (g[i]) fc[ID[i] = ++ id_cnt] = g[i];
_for (i, 1, scc) if (g[i]) SGT.update(root[i], 1, id_cnt, ID[i], g[i]);
_for (i, 1, id_cnt) fc[i] += fc[i - 1];
_for (i, 1, ecnt) if (bel[st[i]] ^ bel[ed[i]] && G2[bel[ed[i]]].find(bel[st[i]]) == G2[bel[ed[i]]].end()) G2[bel[ed[i]]].insert(bel[st[i]]), in_deg[bel[st[i]]] ++ ;
_for (i, 1, scc) if ( ! in_deg[i]) q.push(i);
while (q.size()) {
int u = q.front(); q.pop();
for (int v : G2[u]) {
SGT.merge(root[v], root[u], 1, id_cnt), in_deg[v] -- ;
if ( ! in_deg[v]) q.push(v);
}
} _for (i, 1, n) cout << SGT.tr[root[bel[i]]].val << " "; cout << "\n";
return 0;
}
P9058 [Ynoi2004] rpmtdq / P9678 [ICPC 2022 Jinan R] Tree Distance
容易想到求出支配点对。
点分治。设 \(i < j < k\),且 \(dis_i \le dis_k, dis_j \le dis_k\)。那么 \((i, k)\) 一定不是支配点对(\(i, k\) 位于分治中心不同子树中)。
证明:
\(dis(i, j) \le dis(i) + dis(j) \le dis(i) + dis(k) = dis(i, k)\)
\((i, k)\) 被 \((i, j)\) 支配。
于是在点分治每个连通块时将所有点按照编号排序,然后维护 \(dis\) 的递增的单调栈,每次被 \(u\) 弹出的点的支配点即为 \(u\)。
显然一共只会产生 \(O(n \log n)\) 个支配点对。
然后直接二维数点回答询问即可。复杂度 \(O(n \log^2 n)\)。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define _all(i, a, b) for (int i = (a); i >= (b); i -- )
#define ll long long
#define ld long double
#define VI vector<int>
#define PII pair<int, ll>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
namespace IO {
const int LIM = 1e5;
static char buf[LIM], * p1 = buf, * p2 = buf, obuf[LIM], * p3 = obuf, cc[20];
#define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, LIM, stdin), p1 == p2) ? EOF : * p1 ++ )
#define pc(x) (p3 - obuf < LIM) ? ( * p3 ++ = x) : (fwrite(obuf, p3 - obuf, 1, stdout), p3 = obuf, * p3 ++ = x)
inline int rd() {
int val = 0, sgn = 1; char ch = gc();
for ( ; ch < '0' || ch > '9'; ch = gc()) if (ch == '-') sgn = - 1;
for ( ; ch >= '0' && ch <= '9'; ch = gc()) val = (val << 3) + (val << 1) + (ch ^ 48); return (val * sgn);
} inline void wt(ll x, char c) {
if ( ! x) pc('0'); int len = 0;
if (x < 0) x = - x, pc('-');
while (x) cc[len ++ ] = x % 10 | '0', x /= 10;
while (len -- ) pc(cc[len]); pc(c);
}
} using namespace IO;
const int N = 2e5 + 4, Q = 1e6 + 4; const ll INF = (ll)1e18 + 5;
int n, q, dfn_cnt, cnt, tp, rt, tot, stk[N], vis[N], w[N], sz[N], son[N], dep[N], top[N], dfn[N], rnk[N], fa[N]; ll dis[N], ans[Q]; vector<PII > Tr[N], qry[N]; PII d[N]; VI upd[N];
inline void chkmin(ll & x, ll y) { x = x < y ? x : y; }
inline void chkmax(int & x, int y) { x = x > y ? x : y; }
struct Fenwick_Tree {
ll tr[N];
inline void init() { _for (i, 1, n) tr[i] = INF; }
inline int lowbit(int x) { return (x & ( - x)); }
inline void update(int x, ll k) { for (int i = x; i; i -= lowbit(i)) chkmin(tr[i], k); }
inline ll query(int x) { ll res = INF; for (int i = x; i <= n; i += lowbit(i)) chkmin(res, tr[i]); return res; }
} BIT;
void dfs1(int u) {
sz[u] = 1, son[u] = - 1;
for (PII v : Tr[u]) if (v.fi ^ fa[u]) {
fa[v.fi] = u, dep[v.fi] = dep[u] + 1, dis[v.fi] = dis[u] + v.se, dfs1(v.fi), sz[u] += sz[v.fi];
if ( ! ~ son[u] || sz[son[u]] < sz[v.fi]) son[u] = v.fi;
}
} void dfs2(int u, int Top) {
top[u] = Top;
if ( ~ son[u]) dfs2(son[u], Top);
for (PII v : Tr[u]) if (v.fi ^ son[u] && v.fi ^ fa[u]) dfs2(v.fi, v.fi);
} inline int lca(int u, int v) {
int U = top[u], V = top[v];
while (U ^ V) {
if (dep[U] >= dep[V]) u = fa[U];
else v = fa[V]; U = top[u], V = top[v];
} return (dep[u] < dep[v] ? u : v);
} inline ll dist(int u, int v) { return dis[u] + dis[v] - 2ll * dis[lca(u, v)]; }
void get_sz(int u, int fa) {
sz[u] = 1, cnt ++ ;
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_sz(v.fi, u), sz[u] += sz[v.fi];
} void get_rt(int u, int fa) {
w[u] = cnt - sz[u];
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) chkmax(w[u], sz[v.fi]);
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_rt(v.fi, u);
if (w[u] < w[rt]) rt = u;
} void get_dist(int u, int fa, ll dist) {
d[ ++ tot] = mp(u, dist);
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_dist(v.fi, u, dist + v.se);
} void solve(int u) {
rt = u, cnt = tot = tp = 0, get_sz(u, 0), get_rt(u, 0), get_dist(rt, 0, 0), sort(d + 1, d + tot + 1);
_for (i, 1, tot) { while (tp && d[stk[tp]].se > d[i].se) upd[d[i].fi].pb(d[stk[tp -- ]].fi); stk[ ++ tp] = i; } tp = 0;
_all (i, tot, 1) { while (tp && d[stk[tp]].se > d[i].se) upd[d[stk[tp -- ]].fi].pb(d[i].fi); stk[ ++ tp] = i; } vis[rt] = 1;
for (PII v : Tr[rt]) if ( ! vis[v.fi]) solve(v.fi);
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
n = rd(); int u, v, w;
_for (i, 2, n) u = rd(), v = rd(), w = rd(), Tr[u].pb(mp(v, w)), Tr[v].pb(mp(u, w)); q = rd(), dfs1(1), dfs2(1, 1), solve(1);
_for (i, 1, q) u = rd(), v = rd(), qry[v].pb(mp(i, u)); BIT.init();
_for (i, 1, n) {
for (int j : upd[i]) BIT.update(j, dist(j, i));
for (PII j : qry[i]) ans[j.fi] = BIT.query(j.se);
} _for (i, 1, q) wt(ans[i] ^ INF ? ans[i] : - 1, '\n');
return fwrite(obuf, p3 - obuf, 1, stdout), 0;
}
P3241 [HNOI2015] 开店
点分树板子。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define _all(i, a, b) for (int i = (a); i >= (b); i -- )
#define ll long long
#define ld long double
#define VI vector<int>
#define PII pair<int, ll>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
const int N = 1.5e5 + 4;
int n, q, m, dfn_cnt, cnt, rt, root[2][N], a[N], top[N], sz[N], w[N], son[N], dep[N], dis[N], vis[N], up[N], fa[N]; vector<PII > Tr[N];
inline void chkmin(int & x, int y) { x = x < y ? x : y; }
inline void chkmax(int & x, int y) { x = x > y ? x : y; }
inline PII operator + (PII u, PII v) { return mp(u.fi + v.fi, u.se + v.se); }
inline void operator += (PII & u, PII v) { u = u + v; }
inline PII operator - (PII u, PII v) { return mp(u.fi - v.fi, u.se - v.se); }
inline void operator -= (PII & u, PII v) { u = u - v; }
inline ll calc(PII x, int y) { return x.se + 1ll * x.fi * y; }
struct Node { int w; PII val; }; vector<Node> f[2][N];
void dfs1(int u) {
sz[u] = 1, son[u] = - 1;
for (PII v : Tr[u]) if (v.fi ^ fa[u]) {
dep[v.fi] = dep[u] + 1, dis[v.fi] = dis[u] + v.se, fa[v.fi] = u, dfs1(v.fi), sz[u] += sz[v.fi];
if ( ! ~ son[u] || sz[son[u]] < sz[v.fi]) son[u] = v.fi;
}
} void dfs2(int u, int Top) {
top[u] = Top;
if ( ~ son[u]) dfs2(son[u], Top);
for (PII v : Tr[u]) if (v.fi ^ fa[u] && v.fi ^ son[u]) dfs2(v.fi, v.fi);
} inline int lca(int u, int v) {
int U = top[u], V = top[v];
while (U ^ V) (dep[U] >= dep[V] ? u = fa[U] : v = fa[V]), U = top[u], V = top[v]; return dep[u] <= dep[v] ? u : v;
} inline int dist(int u, int v) { return dis[u] + dis[v] - 2 * dis[lca(u, v)]; }
void get_sz(int u, int fa) {
sz[u] = 1, cnt ++ ;
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_sz(v.fi, u), sz[u] += sz[v.fi];
} void get_rt(int u, int fa) {
w[u] = cnt - sz[u];
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) chkmax(w[u], sz[v.fi]);
for (PII v : Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_rt(v.fi, u);
if (w[u] < w[rt]) rt = u;
} void build(int u, int lst) {
rt = u, cnt = 0, get_sz(u, 0), get_rt(u, 0), up[rt] = lst; int tmp = rt; vis[rt] = 1;
for (PII v : Tr[rt]) if ( ! vis[v.fi]) build(v.fi, tmp);
} inline void upd(int u) {
for (int i = u; i; i = up[i]) f[0][i].pb((Node){a[u], mp(1, dist(i, u))});
for (int i = u; up[i]; i = up[i]) f[1][i].pb((Node){a[u], mp(1, dist(up[i], u))});
} inline PII find(int o, int i, int l, int r) {
int L = 0, R = f[o][i].size() - 1, mid, pos1 = - 1, pos2 = - 1;
while (L <= R) mid = (L + R) >> 1, l <= f[o][i][mid].w ? (pos1 = mid, R = mid - 1) : (L = mid + 1); L = 0, R = f[o][i].size() - 1;
while (L <= R) mid = (L + R) >> 1, f[o][i][mid].w <= r ? (pos2 = mid, L = mid + 1) : (R = mid - 1); return ~ pos1 && ~ pos2 && pos1 <= pos2 ? f[o][i][pos2].val - (pos1 ? f[o][i][pos1 - 1].val : mp(0, 0ll)) : mp(0, 0ll);
} inline ll query(int u, int l, int r) {
int pre = u; ll res = calc(find(0, u, l, r), 0);
for (int i = up[u]; i; pre = up[pre], i = up[i]) res += calc(find(0, i, l, r), dist(i, u)) - calc(find(1, pre, l, r), dist(i, u)); return res;
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> q >> m; int u, v, w; ll x, y, ans = 0;
_for (i, 1, n) cin >> a[i];
_for (i, 2, n) cin >> u >> v >> w, Tr[u].pb(mp(v, w)), Tr[v].pb(mp(u, w)); dfs1(1), dfs2(1, 1), build(1, 0);
_for (i, 1, n) upd(i);
_for (i, 1, n) _for (j, 0, 1) {
sort(f[j][i].begin(), f[j][i].end(), [&] (Node x, Node y) { return x.w < y.w; });
_for (k, 1, (int)f[j][i].size() - 1) f[j][i][k].val += f[j][i][k - 1].val;
} while (q -- ) cin >> u >> x >> y, cout << (ans = query(u, min((x + ans) % m, (y + ans) % m), max((x + ans) % m, (y + ans) % m))) << "\n";
return 0;
}
P6199 [EER1] 河童重工
考虑 Boruvka。
对于第二棵树点分治,对于一个联通块内的点,建出第一棵树的虚树,然后树形 DP 即可求出距离每个点最近的点(由于是 Boruvka 要存最近和次近)。
直接做复杂度是 \(O(n \log^3 n)\) 的:Boruvka、点分治、虚树的 sort 各一只 \(\log\)。
由于 Boruvka 每一层跑点分治时建出来的虚树是相同的,直接预处理出虚树即可。
复杂度 \(O(n \log^2 n)\),常数较大。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define _all(i, a, b) for (int i = (a); i >= (b); i -- )
#define ll long long
#define VI vector<int>
#define PII pair<int, ll>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
const int N = 4e5 + 4;
int n, k, rt, top, id_cnt, Root, e[N], root[N], a[N], col[N], vis[N], stk[N], sgn[N], imp[N], sz[N], w[N], id[N], up[N]; ll ans, dis[N]; vector<ll> W[N]; vector<PII > E[N]; vector<int> T[N], vec[N];
inline void chkmin(ll & x, ll y) { x = x < y ? x : y; }
inline void chkmax(int & x, int y) { x = x > y ? x : y; }
struct Node { int cnt; PII val[2]; } g[N], f[N], F[N], res[N]; PII cur;
inline Node operator + (Node x, PII t) {
if ( ! x.cnt) x.cnt ++ , x.val[0] = t;
else if (x.cnt == 1) {
if (x.val[0].fi == t.fi) chkmin(x.val[0].se, t.se);
else x.cnt ++ , x.val[1] = t;
} else {
if (x.val[0].fi == t.fi) chkmin(x.val[0].se, t.se);
else if (x.val[1].fi == t.fi) chkmin(x.val[1].se, t.se);
else {
if (t.se <= x.val[0].se) x.val[1] = x.val[0], x.val[0] = t;
else if (t.se <= x.val[1].se) x.val[1] = t;
}
} if (x.cnt == 2 && x.val[0].se > x.val[1].se) swap(x.val[0], x.val[1]); return x;
} inline void operator += (Node & x, PII t) { x = x + t; }
inline Node operator + (Node x, Node t) { _for (i, 0, t.cnt - 1) x += t.val[i]; return x; }
inline void operator += (Node & x, Node t) { x = x + t; }
struct DSU {
int fa[N], sz[N];
inline void init() { _for (i, 1, n) fa[i] = i, sz[i] = 1; }
int find(int u) { return fa[u] == u ? u : fa[u] = find(fa[u]); }
inline bool con(int u, int v) { return find(u) == find(v); }
inline void merge(int u, int v) {
u = find(u), v = find(v);
if (sz[u] > sz[v]) swap(u, v); fa[u] = v, sz[v] += sz[u];
}
} DSU;
struct Tree {
int dfn_cnt, wjw, in[N], out[N], sz[N], son[N], dep[N], top[N], dfn[N], rnk[N], fa[N]; vector<PII > Tr[N]; ll dis[N];
void dfs1(int u) {
sz[u] = 1, son[u] = - 1, in[u] = ++ wjw;
for (PII v : Tr[u]) if (v.fi ^ fa[u]) {
fa[v.fi] = u, dep[v.fi] = dep[u] + 1, dis[v.fi] = dis[u] + v.se, dfs1(v.fi), sz[u] += sz[v.fi];
if ( ! ~ son[u] || sz[son[u]] < sz[v.fi]) son[u] = v.fi;
} out[u] = ++ wjw;
} void dfs2(int u, int Top) {
rnk[dfn[u] = ++ dfn_cnt] = u, top[u] = Top;
if ( ~ son[u]) dfs2(son[u], Top);
for (PII v : Tr[u]) if (v.fi ^ son[u] && v.fi ^ fa[u]) dfs2(v.fi, v.fi);
} inline int lca(int u, int v) {
int U = top[u], V = top[v];
while (U ^ V) {
if (dep[U] >= dep[V]) u = fa[U];
else v = fa[V]; U = top[u], V = top[v];
} return (dep[u] < dep[v] ? u : v);
} inline int dist(int u, int v) { return dis[u] + dis[v] - 2 * dis[lca(u, v)]; }
} T1, T2, T3;
void get_sz(int u, int fa) {
sz[u] = 1, a[ ++ k] = u, sgn[u] = imp[u] = 1;
for (PII v : T2.Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_sz(v.fi, u), sz[u] += sz[v.fi];
} void get_rt(int u, int fa) {
w[u] = k - sz[u];
for (PII v : T2.Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) chkmax(w[u], sz[v.fi]);
for (PII v : T2.Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_rt(v.fi, u);
if (w[u] < w[rt]) rt = u;
} void get_dis(int u, int fa, ll d) { dis[u] = d; for (PII v : T2.Tr[u]) if (v.fi ^ fa && ! vis[v.fi]) get_dis(v.fi, u, d + v.se); }
void dfs1(int u) {
g[u].cnt = f[u].cnt = 0;
if (imp[u]) g[u] += mp(col[u], dis[u] + T3.dis[u]);
for (PII v : T3.Tr[u]) dfs1(v.fi), g[u] += g[v.fi];
} void dfs2(int u, Node t) {
_for (i, 0, g[u].cnt - 1) g[u].val[i].se -= T3.dis[u] << 1; g[u] += t, f[u] = g[u];
_for (i, 0, f[u].cnt - 1) f[u].val[i].se += dis[u] + T3.dis[u];
if (imp[u]) res[u] += f[u];
for (PII v : T3.Tr[u]) dfs2(v.fi, g[u]);
} void build(int u, int lst) {
rt = u, k = top = 0, get_sz(u, 0), get_rt(u, 0), dis[rt] = 0; int t = k, x; sort(a + 1, a + k + 1, [&] (int x, int y) { return T1.in[x] < T1.in[y]; });
_for (i, 1, k) vec[rt].pb(a[i]);
for (PII v : T2.Tr[rt]) if ( ! vis[v.fi]) get_dis(v.fi, rt, v.se);
_for (i, 1, t - 1) if ( ! sgn[x = T1.lca(a[i], a[i + 1])]) sgn[x] = 1, a[ ++ k] = x;
_for (i, 1, k) a[i + k] = - a[i]; k <<= 1, sort(a + 1, a + k + 1, [&] (int x, int y) { return (x > 0 ? T1.in[x] : T1.out[ - x]) < (y > 0 ? T1.in[y] : T1.out[ - y]); }), W[rt].resize(k + 4), E[rt].resize(k + 4), root[rt] = a[1];
_for (i, 1, k) {
if (a[i] > 0) stk[ ++ top] = a[i];
else {
x = stk[top -- ];
if (top) E[rt][ ++ e[rt]] = mp(stk[top], x), W[rt][e[rt]] = T1.dist(stk[top], x);
}
} _for (i, 1, k) if (a[i] > 0) sgn[a[i]] = imp[a[i]] = 0; vis[rt] = 1, T[lst].pb(rt), up[rt] = lst; int tmp = rt;
if ( ! Root) Root = rt;
for (PII v : T2.Tr[rt]) if ( ! vis[v.fi]) build(v.fi, tmp);
} void solve(int u) {
for (int i : vec[u]) imp[i] = 1; dis[u] = 0;
for (PII v : T2.Tr[u]) if ( ! vis[v.fi]) get_dis(v.fi, u, v.se);
_for (i, 1, e[u]) T3.Tr[E[u][i].fi].pb(mp(E[u][i].se, W[u][i])); T3.dfs1(root[u]), dfs1(root[u]), dfs2(root[u], (Node){0, {mp(0, 0), mp(0, 0)}});
_for (i, 1, e[u]) T3.Tr[E[u][i].fi].clear(), T3.Tr[E[u][i].se].clear(), T3.dis[E[u][i].fi] = T3.dep[E[u][i].fi] = T3.dis[E[u][i].se] = T3.dep[E[u][i].se] = 0; T3.wjw = 0, vis[u] = 1;
for (int i : vec[u]) imp[i] = 0;
for (int v : T[u]) solve(v);
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n, DSU.init(); int u, v, w;
_for (i, 2, n) cin >> u >> v >> w, T1.Tr[u].pb(mp(v, w)), T1.Tr[v].pb(mp(u, w)); T1.dfs1(1), T1.dfs2(1, 1);
_for (i, 2, n) cin >> u >> v >> w, T2.Tr[u].pb(mp(v, w)), T2.Tr[v].pb(mp(u, w)); T2.dfs1(1), T2.dfs2(1, 1); build(1, 0);
while (DSU.sz[DSU.find(1)] < n) {
_for (i, 1, n) col[i] = DSU.find(i), vis[i] = 0, F[i].cnt = f[i].cnt = g[i].cnt = res[i].cnt = 0; id_cnt = 0, solve(Root);
_for (i, 1, n) F[col[i]] += res[i];
_for (i, 1, n) if (DSU.find(i) == i) id[ ++ id_cnt] = i;
_for (i, 1, id_cnt) {
cur = mp(0, 0);
_for (j, 0, F[id[i]].cnt - 1) if (F[id[i]].val[j].fi ^ id[i]) { cur = F[id[i]].val[j]; break; }
if (cur.fi && cur.se && ! DSU.con(cur.fi, id[i])) DSU.merge(cur.fi, id[i]), ans += cur.se;
}
} cout << ans << "\n";
return 0;
}
网络流相关
P4897 【模板】最小割树(Gomory-Hu Tree)
最小割树。
初始的点集 \(S = \{1, 2, \cdots, n\}\),我们从中随便找两个点充当源点和汇点 \(S\) 和 \(T\),并用整张图跑 flow,求出 \(S \to T\) 的最小割 \(w\)。
然后 \(u, v\) 之间连一条边权为 \(w\) 的边。由于求最小割将 \(S\) 划分为了两个集合 \(S_1, S_2\),递归 \(S_1, S_2\) 即可。
容易发现最后连出来的东西是一棵树。\(u, v\) 两点的最小割即为树上路径的权值最小值。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define PII pair<int, int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define VI vector<int>
using namespace std;
const int N = 505, M = 1505, INF = 1e9 + 5;
int n, m, q, dfn_cnt, a[N], sz[N], vis[N], son[N], dep[N], top[N], dfn[N], rnk[N], fa[N]; vector<PII > Tr[N]; vector<int> vec1, vec2;
inline void chkmin(int & x, int y) { x = x < y ? x : y; }
struct Edge { int u, v, w; } e[M];
struct Sparse_Table {
int Log[N], ST[20][N];
inline void init(int * a) {
Log[0] = - 1;
_for (i, 1, n) Log[i] = Log[i >> 1] + 1, ST[0][i] = a[rnk[i]];
_for (k, 1, Log[n]) _for (i, 1, n - (1 << k) + 1) ST[k][i] = min(ST[k - 1][i], ST[k - 1][i + (1 << (k - 1))]);
} inline int query(int l, int r) { if (l > r) return INF; int k = Log[r - l + 1]; return min(ST[k][l], ST[k][r - (1 << k) + 1]); }
} ST;
struct Flow {
int S, T, ecnt, flow, hd[N], cur[N], dis[N]; queue<int> q;
struct Edge { int to, nxt, cap, flow; } e[M << 2];
inline void _add(int u, int v, int w) { e[ecnt] = (Edge){v, hd[u], w, 0}, hd[u] = ecnt ++ ; }
inline void add_edge(int u, int v, int w) { _add(u, v, w), _add(v, u, 0); }
inline void init() { ecnt = flow = 0, memset(hd, - 1, sizeof(hd)); }
inline bool bfs() {
_for (i, 1, n) dis[i] = 0; q.push(S), dis[S] = 1; vec1.clear(), vec2.clear();
if (vis[S]) vec1.pb(S);
while (q.size()) {
int u = q.front(); q.pop();
for (int i = hd[u], v; ~ i; i = e[i].nxt) {
v = e[i].to;
if (e[i].cap > e[i].flow && ! dis[v]) { dis[v] = dis[u] + 1, q.push(v); if (vis[v]) vec1.pb(v); }
}
} _for (i, 1, n) if ( ! dis[i] && vis[i]) vec2.pb(i); return dis[T];
} int dfs(int u, int flow) {
if ( ! flow || u == T) return flow; int res = 0;
for (int & i = cur[u], v, f; ~ i; i = e[i].nxt) {
v = e[i].to;
if (e[i].cap > e[i].flow && dis[v] == dis[u] + 1) f = dfs(v, min(flow - res, e[i].cap - e[i].flow)), res += f, e[i].flow += f, e[i ^ 1].flow -= f;
if (res == flow) return res;
} return res;
} inline int dinic() {
int f;
while (vec1.clear(), vec2.clear(), bfs()) {
memcpy(cur, hd, sizeof(hd));
while ((f = dfs(S, INF)) > 0) flow += f;
} return flow;
}
} Flow;
void solve(VI vec) {
if (vec.size() <= 1) return ; Flow.init(), Flow.S = vec[0], Flow.T = vec[1]; VI v1, v2;
for (int i : vec) vis[i] = 1;
_for (i, 1, m) Flow.add_edge(e[i].u, e[i].v, e[i].w), Flow.add_edge(e[i].v, e[i].u, e[i].w); int val = Flow.dinic(); v1 = vec1, v2 = vec2, Tr[vec[0]].pb(mp(vec[1], val)), Tr[vec[1]].pb(mp(vec[0], val));
for (int i : vec) vis[i] = 0; solve(v1), solve(v2);
} void dfs1(int u) {
sz[u] = 1, son[u] = - 1;
for (PII v : Tr[u]) if (v.fi ^ fa[u]) {
fa[v.fi] = u, dep[v.fi] = dep[u] + 1, a[v.fi] = v.se, dfs1(v.fi), sz[u] += sz[v.fi];
if ( ! ~ son[u] || sz[son[u]] < sz[v.fi]) son[u] = v.fi;
}
} void dfs2(int u, int Top) {
rnk[dfn[u] = ++ dfn_cnt] = u, top[u] = Top;
if ( ~ son[u]) dfs2(son[u], Top);
for (PII v : Tr[u]) if (v.fi ^ son[u] && v.fi ^ fa[u]) dfs2(v.fi, v.fi);
} inline int query(int u, int v) {
int res = INF, U = top[u], V = top[v];
while (U ^ V) {
if (dep[U] >= dep[V]) chkmin(res, ST.query(dfn[U], dfn[u])), u = fa[U];
else chkmin(res, ST.query(dfn[V], dfn[v])), v = fa[V]; U = top[u], V = top[v];
} return min(res, ST.query(min(dfn[u], dfn[v]) + 1, max(dfn[u], dfn[v])));
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m, n ++ ; int u, v; VI vec;
_for (i, 1, m) cin >> e[i].u >> e[i].v >> e[i].w, e[i].u ++ , e[i].v ++ ;
_for (i, 1, n) vec.pb(i); solve(vec), dfs1(1), dfs2(1, 1), ST.init(a), cin >> q;
while (q -- ) cin >> u >> v, cout << query(u + 1, v + 1) << "\n";
return 0;
}
P3329 [ZJOI2011] 最小割 / P4123 [CQOI2016] 不同的最小割
最小割树板子。
统计答案直接 \(O(n^2)\) 枚举点对即可。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define int long long
#define PII pair<int, int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define VI vector<int>
using namespace std;
const int N = 155, M = 3005, INF = (int)1e18 + 5;
int n, m, q, dfn_cnt, a[N], sz[N], vis[N], son[N], dep[N], d[N], top[N], dfn[N], rnk[N], fa[N]; vector<PII > Tr[N]; vector<int> vec1, vec2;
inline void chkmin(int & x, int y) { x = x < y ? x : y; }
struct Edge { int u, v, w; } e[M];
struct Sparse_Table {
int Log[N], ST[20][N];
inline void init(int * a) {
Log[0] = - 1;
_for (i, 1, n) Log[i] = Log[i >> 1] + 1, ST[0][i] = a[rnk[i]];
_for (k, 1, Log[n]) _for (i, 1, n - (1 << k) + 1) ST[k][i] = min(ST[k - 1][i], ST[k - 1][i + (1 << (k - 1))]);
} inline int query(int l, int r) { if (l > r) return INF; int k = Log[r - l + 1]; return min(ST[k][l], ST[k][r - (1 << k) + 1]); }
} ST;
struct Flow {
int S, T, ecnt, flow, hd[N], cur[N], dis[N]; queue<int> q;
struct Edge { int to, nxt, cap, flow; } e[M << 2];
inline void _add(int u, int v, int w) { e[ecnt] = (Edge){v, hd[u], w, 0}, hd[u] = ecnt ++ ; }
inline void add_edge(int u, int v, int w) { _add(u, v, w), _add(v, u, 0); }
inline void init() { ecnt = flow = 0, memset(hd, - 1, sizeof(hd)); }
inline bool bfs() {
_for (i, 1, n) dis[i] = 0; q.push(S), dis[S] = 1; vec1.clear(), vec2.clear();
if (vis[S]) vec1.pb(S);
while (q.size()) {
int u = q.front(); q.pop();
for (int i = hd[u], v; ~ i; i = e[i].nxt) {
v = e[i].to;
if (e[i].cap > e[i].flow && ! dis[v]) { dis[v] = dis[u] + 1, q.push(v); if (vis[v]) vec1.pb(v); }
}
} _for (i, 1, n) if ( ! dis[i] && vis[i]) vec2.pb(i); return dis[T];
} int dfs(int u, int flow) {
if ( ! flow || u == T) return flow; int res = 0;
for (int & i = cur[u], v, f; ~ i; i = e[i].nxt) {
v = e[i].to;
if (e[i].cap > e[i].flow && dis[v] == dis[u] + 1) f = dfs(v, min(flow - res, e[i].cap - e[i].flow)), res += f, e[i].flow += f, e[i ^ 1].flow -= f;
if (res == flow) return res;
} return res;
} inline int dinic() {
int f;
while (vec1.clear(), vec2.clear(), bfs()) {
memcpy(cur, hd, sizeof(hd));
while ((f = dfs(S, INF)) > 0) flow += f;
} return flow;
}
} Flow;
void solve(VI vec) {
if (vec.size() <= 1) return ; Flow.init(), Flow.S = vec[0], Flow.T = vec[1]; VI v1, v2;
for (int i : vec) vis[i] = 1;
_for (i, 1, m) Flow.add_edge(e[i].u, e[i].v, e[i].w), Flow.add_edge(e[i].v, e[i].u, e[i].w); int val = Flow.dinic(); v1 = vec1, v2 = vec2, Tr[vec[0]].pb(mp(vec[1], val)), Tr[vec[1]].pb(mp(vec[0], val));
for (int i : vec) vis[i] = 0; solve(v1), solve(v2);
} void dfs1(int u) {
sz[u] = 1, son[u] = - 1;
for (PII v : Tr[u]) if (v.fi ^ fa[u]) {
fa[v.fi] = u, dep[v.fi] = dep[u] + 1, d[v.fi] = d[u] + v.se, a[v.fi] = v.se, dfs1(v.fi), sz[u] += sz[v.fi];
if ( ! ~ son[u] || sz[son[u]] < sz[v.fi]) son[u] = v.fi;
}
} void dfs2(int u, int Top) {
rnk[dfn[u] = ++ dfn_cnt] = u, top[u] = Top;
if ( ~ son[u]) dfs2(son[u], Top);
for (PII v : Tr[u]) if (v.fi ^ son[u] && v.fi ^ fa[u]) dfs2(v.fi, v.fi);
} inline int query(int u, int v) {
int res = INF, U = top[u], V = top[v];
while (U ^ V) {
if (dep[U] >= dep[V]) chkmin(res, ST.query(dfn[U], dfn[u])), u = fa[U];
else chkmin(res, ST.query(dfn[V], dfn[v])), v = fa[V]; U = top[u], V = top[v];
} return min(res, ST.query(min(dfn[u], dfn[v]) + 1, max(dfn[u], dfn[v])));
} inline void solve() {
cin >> n >> m, dfn_cnt = fa[1] = 0; int u, ans; VI vec;
_for (i, 1, m) cin >> e[i].u >> e[i].v >> e[i].w;
_for (i, 1, n) vec.pb(i), Tr[i].clear(); solve(vec), dfs1(1), dfs2(1, 1), ST.init(a), cin >> q;
while (q -- ) {
cin >> u, ans = 0;
_for (i, 1, n) _for (j, i + 1, n) ans += query(i, j) <= u; cout << ans << "\n";
} cout << "\n";
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T; cin >> T; while (T -- ) solve();
return 0;
}
CF343E Pumping Stations
差一点。
建出最小割树,考虑树上权值最小的一条边,让他只经过一次是最优的(也显然是可行的),然后分成两个小集合继续做即可。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define int long long
#define PII pair<int, int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define VI vector<int>
using namespace std;
const int N = 855, M = 8505, INF = (int)1e18 + 5;
int n, m, q, dfn_cnt, a[N], col[N], sz[N], vis[N], son[N], dep[N], top[N], dfn[N], rnk[N], fa[N]; vector<PII > Tr[N], f[N]; vector<int> vec1, vec2, ans;
inline void chkmin(int & x, int y) { x = x < y ? x : y; }
struct Edge { int u, v, w; } e[M];
struct Sparse_Table {
int Log[N], ST[20][N];
inline void init(int * a) {
Log[0] = - 1;
_for (i, 1, n) Log[i] = Log[i >> 1] + 1, ST[0][i] = a[rnk[i]];
_for (k, 1, Log[n]) _for (i, 1, n - (1 << k) + 1) ST[k][i] = min(ST[k - 1][i], ST[k - 1][i + (1 << (k - 1))]);
} inline int query(int l, int r) { if (l > r) return INF; int k = Log[r - l + 1]; return min(ST[k][l], ST[k][r - (1 << k) + 1]); }
} ST;
struct Flow {
int S, T, ecnt, flow, hd[N], cur[N], dis[N]; queue<int> q;
struct Edge { int to, nxt, cap, flow; } e[M << 2];
inline void _add(int u, int v, int w) { e[ecnt] = (Edge){v, hd[u], w, 0}, hd[u] = ecnt ++ ; }
inline void add_edge(int u, int v, int w) { _add(u, v, w), _add(v, u, 0); }
inline void init() { ecnt = flow = 0, memset(hd, - 1, sizeof(hd)); }
inline bool bfs() {
_for (i, 1, n) dis[i] = 0; q.push(S), dis[S] = 1; vec1.clear(), vec2.clear();
if (vis[S]) vec1.pb(S);
while (q.size()) {
int u = q.front(); q.pop();
for (int i = hd[u], v; ~ i; i = e[i].nxt) {
v = e[i].to;
if (e[i].cap > e[i].flow && ! dis[v]) { dis[v] = dis[u] + 1, q.push(v); if (vis[v]) vec1.pb(v); }
}
} _for (i, 1, n) if ( ! dis[i] && vis[i]) vec2.pb(i); return dis[T];
} int dfs(int u, int flow) {
if ( ! flow || u == T) return flow; int res = 0;
for (int & i = cur[u], v, f; ~ i; i = e[i].nxt) {
v = e[i].to;
if (e[i].cap > e[i].flow && dis[v] == dis[u] + 1) f = dfs(v, min(flow - res, e[i].cap - e[i].flow)), res += f, e[i].flow += f, e[i ^ 1].flow -= f;
if (res == flow) return res;
} return res;
} inline int dinic() {
int f;
while (vec1.clear(), vec2.clear(), bfs()) {
memcpy(cur, hd, sizeof(hd));
while ((f = dfs(S, INF)) > 0) flow += f;
} return flow;
}
} Flow;
void solve(VI vec) {
if (vec.size() <= 1) return ; Flow.init(), Flow.S = vec[0], Flow.T = vec[1]; VI v1, v2;
for (int i : vec) vis[i] = 1;
_for (i, 1, m) Flow.add_edge(e[i].u, e[i].v, e[i].w), Flow.add_edge(e[i].v, e[i].u, e[i].w); int val = Flow.dinic(); v1 = vec1, v2 = vec2, Tr[vec[0]].pb(mp(vec[1], val)), Tr[vec[1]].pb(mp(vec[0], val));
for (int i : vec) vis[i] = 0; solve(v1), solve(v2);
} void dfs1(int u) {
sz[u] = 1, son[u] = - 1;
for (PII v : Tr[u]) if (v.fi ^ fa[u]) {
fa[v.fi] = u, dep[v.fi] = dep[u] + 1, a[v.fi] = v.se, dfs1(v.fi), sz[u] += sz[v.fi];
if ( ! ~ son[u] || sz[son[u]] < sz[v.fi]) son[u] = v.fi;
}
} void dfs2(int u, int Top) {
rnk[dfn[u] = ++ dfn_cnt] = u, top[u] = Top;
if ( ~ son[u]) dfs2(son[u], Top);
for (PII v : Tr[u]) if (v.fi ^ son[u] && v.fi ^ fa[u]) dfs2(v.fi, v.fi);
} inline int query(int u, int v) {
int res = INF, U = top[u], V = top[v];
while (U ^ V) {
if (dep[U] >= dep[V]) chkmin(res, ST.query(dfn[U], dfn[u])), u = fa[U];
else chkmin(res, ST.query(dfn[V], dfn[v])), v = fa[V]; U = top[u], V = top[v];
} return min(res, ST.query(min(dfn[u], dfn[v]) + 1, max(dfn[u], dfn[v])));
} void dfs(int u, VI & vec) {
vec.pb(u);
for (PII v : Tr[u]) if (vis[v.fi] && ! col[v.fi]) col[v.fi] = col[u], dfs(v.fi, vec);
} inline int gans(VI vec) {
if (vec.size() <= 1) return ans.pb(vec[0]), 0; int rt1 = 0, rt2 = 0, cur = INF; VI vec1, vec2;
for (int u : vec) vis[u] = 1;
for (int u : vec) for (PII v : Tr[u]) if (vis[v.fi] && v.se < cur) cur = v.se, rt1 = u, rt2 = v.fi; f[rt1].pb(mp(rt2, cur)), f[rt2].pb(mp(rt1, cur)), col[rt1] = rt1, col[rt2] = rt2, dfs(rt1, vec1), dfs(rt2, vec2);
for (int u : vec) vis[u] = col[u] = 0; return gans(vec1) + gans(vec2) + cur;
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m; VI vec;
_for (i, 1, m) cin >> e[i].u >> e[i].v >> e[i].w;
_for (i, 1, n) vec.pb(i); solve(vec), dfs1(1), dfs2(1, 1), ST.init(a), cout << gans(vec) << "\n";
for (int i : ans) cout << i << " "; cout << "\n";
return 0;
}
P3729 曼哈顿计划EX
有 \(k\) 条互不相交的路径,就是最大流为 \(k\)。
建出最小割树,将最小割树上的边从大到小加入,带权并查集维护每个连通块的点权和 以及 当前点权和最大的联通块的权值 \(w\)。
\(w \ge x\),说明可以选出权值和至少为 \(x\) 的点集使得路径条数至少为 \(k\)。每加入一条边后更新答案即可。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define int long long
#define pb push_back
#define VI vector<int>
using namespace std;
const int N = 555, M = 3005, INF = (int)1e18 + 5;
int n, m, q, dfn_cnt, ecnt, W, a[N], w[N], sz[N], vis[N], son[N], dep[N], top[N], dfn[N], rnk[N], fa[N]; vector<int> vec1, vec2;
struct Edge { int u, v, w; } e[M], E[N];
struct DSU {
int fa[N], val[N];
inline void init() { _for (i, 1, n) fa[i] = i, W = max(W, val[i] = w[i]); }
inline int find(int u) { return fa[u] == u ? u : fa[u] = find(fa[u]); }
inline void merge(int u, int v) {
u = find(u), v = find(v);
if (u ^ v) fa[u] = v, W = max(W, val[v] += val[u]);
}
} DSU;
struct Flow {
int S, T, ecnt, flow, hd[N], cur[N], dis[N]; queue<int> q;
struct Edge { int to, nxt, cap, flow; } e[M << 2];
inline void _add(int u, int v, int w) { e[ecnt] = (Edge){v, hd[u], w, 0}, hd[u] = ecnt ++ ; }
inline void add_edge(int u, int v, int w) { _add(u, v, w), _add(v, u, 0); }
inline void init() { ecnt = flow = 0, memset(hd, - 1, sizeof(hd)); }
inline bool bfs() {
_for (i, 1, n) dis[i] = 0; q.push(S), dis[S] = 1; vec1.clear(), vec2.clear();
if (vis[S]) vec1.pb(S);
while (q.size()) {
int u = q.front(); q.pop();
for (int i = hd[u], v; ~ i; i = e[i].nxt) {
v = e[i].to;
if (e[i].cap > e[i].flow && ! dis[v]) { dis[v] = dis[u] + 1, q.push(v); if (vis[v]) vec1.pb(v); }
}
} _for (i, 1, n) if ( ! dis[i] && vis[i]) vec2.pb(i); return dis[T];
} int dfs(int u, int flow) {
if ( ! flow || u == T) return flow; int res = 0;
for (int & i = cur[u], v, f; ~ i; i = e[i].nxt) {
v = e[i].to;
if (e[i].cap > e[i].flow && dis[v] == dis[u] + 1) f = dfs(v, min(flow - res, e[i].cap - e[i].flow)), res += f, e[i].flow += f, e[i ^ 1].flow -= f;
if (res == flow) return res;
} return res;
} inline int dinic() {
int f;
while (vec1.clear(), vec2.clear(), bfs()) {
memcpy(cur, hd, sizeof(hd));
while ((f = dfs(S, INF)) > 0) flow += f;
} return flow;
}
} Flow;
void solve(VI vec) {
if (vec.size() <= 1) return ; Flow.init(), Flow.S = vec[0], Flow.T = vec[1]; VI v1, v2;
for (int i : vec) vis[i] = 1;
_for (i, 1, m) Flow.add_edge(e[i].u, e[i].v, e[i].w), Flow.add_edge(e[i].v, e[i].u, e[i].w); E[ ++ ecnt] = (Edge){vec[0], vec[1], Flow.dinic()}, v1 = vec1, v2 = vec2;
for (int i : vec) vis[i] = 0; solve(v1), solve(v2);
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n >> m >> q; int x, ok; VI vec;
_for (i, 1, n) cin >> w[i]; DSU.init();
_for (i, 1, m) cin >> e[i].u >> e[i].v, e[i].w = 1;
_for (i, 1, n) vec.pb(i); solve(vec), sort(E + 1, E + ecnt + 1, [&] (Edge x, Edge y) { return x.w > y.w; });
while (q -- ) {
W = 0, DSU.init(), cin >> x, ok = 0;
if (W >= x) { cout << "nan\n"; continue; }
_for (i, 1, ecnt) {
DSU.merge(E[i].u, E[i].v);
if (W >= x) { ok = 1, cout << E[i].w << "\n"; break; }
} if ( ! ok) cout << "Nuclear launch detected\n";
}
return 0;
}
P3231 [HNOI2013] 消毒
首先考虑二维问题:可以选择 \(x \times y\) 的矩形消毒,花费为 \(\min(x, y)\),求最小花费多少消毒。
考虑覆盖一个 \(x \times y\) 的矩形,可以想象成覆盖 \(x\) 个 \(1 \times y\) 或者 \(y\) 个 \(x \times 1\)。显然花费是不变的。
假设 \(x \le y\),如果固定了 \(x\),那么 \(y\) 取到最大值 \(n\) 一定不劣(花费不变,覆盖的点变多)。
那么对于一个需要被消毒的格子 \((x, y)\),我们需要 \(x\) 这一行被覆盖或者 \(y\) 这一列被覆盖。
转化为一张二分图,第 \(x\) 行向第 \(y\) 列连边,最终的最小点覆盖即为答案。
二分图最小点覆盖等于最大流。于是 flow 求解即可。
转到三维,发现 \(abc \le 5000\),所以 \(\min\{a, b, c\} \le \sqrt [3] {5000} \approx 17\),于是直接 \(2^{17}\) 枚举每一层选不选,然后跑二分图即可。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
using namespace std;
const int N = 5005, M = 75 * N, INF = 1e9 + 5;
int A, B, C, a[20][75][N], vis[75][N];
inline void chkmin(int & x, int y) { x = x < y ? x : y; }
struct Flow {
int S, T, ecnt, hd[N], cur[N], d[N]; queue<int> q;
struct Edge { int nxt, to, cap, flow; } e[M << 2];
inline void init() { ecnt = 0, memset(hd, - 1, sizeof(hd)); }
inline void _add(int u, int v, int w) { e[ecnt] = (Edge){hd[u], v, w, 0}, hd[u] = ecnt ++ ; }
inline void add_edge(int u, int v, int w) { _add(u, v, w), _add(v, u, 0); }
inline bool bfs() {
_for (i, S, T) d[i] = 0; q.push(S), d[S] = 1;
while (q.size()) {
int u = q.front(); q.pop();
for (int i = hd[u], v; ~ i; i = e[i].nxt) {
v = e[i].to;
if ( ! d[v] && e[i].cap > e[i].flow) d[v] = d[u] + 1, q.push(v);
}
} return d[T];
} int dfs(int u, int flow) {
if (u == T || ! flow) return flow; int res = 0;
for (int & i = cur[u], v, f; ~ i; i = e[i].nxt) {
v = e[i].to;
if (e[i].cap > e[i].flow && d[v] == d[u] + 1) e[i].flow += f = dfs(v, min(flow - res, e[i].cap - e[i].flow)), e[i ^ 1].flow -= f, res += f;
if (flow == res) break;
} return res;
} inline int dinic() {
int ans = 0, f;
while (bfs()) {
_for (i, S, T) cur[i] = hd[i];
while ((f = dfs(S, INF))) ans += f;
} return ans;
}
} Flow;
inline void solve() {
cin >> A >> B >> C; int ans = INF;
_for (i, 1, A) _for (j, 1, B) _for (k, 1, C) cin >> a[i][j][k]; Flow.S = 0, Flow.T = B + C + 1;
_for (S, 0, (1 << A) - 1) {
Flow.init();
_for (i, 1, B) Flow.add_edge(Flow.S, i, 1);
_for (i, 1, C) Flow.add_edge(i + B, Flow.T, 1);
_for (i, 1, B) _for (j, 1, C) vis[i][j] = 0;
_for (i, 1, A) if ( ! ((S >> (i - 1)) & 1)) _for (j, 1, B) _for (k, 1, C) if (a[i][j][k] && ! vis[j][k]) vis[j][k] = 1, Flow.add_edge(j, k + B, 1); chkmin(ans, __builtin_popcount(S) + Flow.dinic());
} cout << ans << "\n";
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T; cin >> T; while (T -- ) solve();
return 0;
}
树上启发式合并(dsu on tree)
U41492 树上数颜色
思想很简单:
-
对于一个点 \(u\),先遍历其轻儿子,并不保留贡献;
-
遍历重儿子,并保留贡献;
-
再次遍历轻儿子,保留贡献;
-
如果当前 \(u\) 不保留贡献,则撤去子树内所有点。
复杂度是 \(O(n \log n)\) 的,证明基于:如果点 \(u\) 遍历 \(x\) 次,则重儿子遍历 \(x\) 次,轻儿子遍历 \(2x\) 次。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define ll long long
#define pb push_back
using namespace std;
const int N = 4e5 + 4;
int n, q, cur_ans, dfn_cnt, ans[N], a[N], cnt[N], sz[N], son[N], dfn[N], rnk[N]; vector<int> Tr[N];
inline void add(int x) { cur_ans += ! (cnt[a[x]] ++ ); }
inline void del(int x) { cur_ans -= ! ( -- cnt[a[x]]); }
void dfs1(int u, int fa) {
rnk[dfn[u] = ++ dfn_cnt] = u, sz[u] = 1, son[u] = - 1;
for (int v : Tr[u]) if (v ^ fa) {
dfs1(v, u), sz[u] += sz[v];
if ( ! ~ son[u] || sz[son[u]] < sz[v]) son[u] = v;
}
} void dfs2(int u, int fa, int keep) {
for (int v : Tr[u]) if (v ^ fa && v ^ son[u]) dfs2(v, u, 0);
if ( ~ son[u]) dfs2(son[u], u, 1);
for (int v : Tr[u]) if (v ^ fa && v ^ son[u]) _for (i, dfn[v], dfn[v] + sz[v] - 1) add(rnk[i]); add(u), ans[u] = cur_ans;
if ( ! keep) _for (i, dfn[u], dfn[u] + sz[u] - 1) del(rnk[i]);
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n; int u, v;
_for (i, 2, n) cin >> u >> v, Tr[u].pb(v), Tr[v].pb(u);
_for (i, 1, n) cin >> a[i]; dfs1(1, 0), dfs2(1, 0, 1), cin >> q;
while (q -- ) cin >> u, cout << ans[u] << "\n";
return 0;
}
CF600E Lomsat gelral
用桶记录每种出现次数的编号和即可。bitset 查询最大值。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define ll long long
#define pb push_back
using namespace std;
const int N = 4e5 + 4;
int n, dfn_cnt, a[N], dfn[N], rnk[N], sz[N], son[N], cnt[N]; bitset<N> occ; vector<int> Tr[N]; ll s[N], ans[N];
inline void upd(int x, int k) {
if (cnt[x]) s[cnt[x]] -= x;
if (cnt[x] && ! s[cnt[x]]) occ[n - cnt[x] + 1] = 0; cnt[x] += k;
if (cnt[x] && ! s[cnt[x]]) occ[n - cnt[x] + 1] = 1;
if (cnt[x]) s[cnt[x]] += x;
} void dfs1(int u, int fa) {
sz[u] = 1, son[u] = - 1, rnk[dfn[u] = ++ dfn_cnt] = u;
for (int v : Tr[u]) if (v ^ fa) {
dfs1(v, u), sz[u] += sz[v];
if ( ! ~ son[u] || sz[son[u]] < sz[v]) son[u] = v;
}
} void dfs2(int u, int fa, bool keep) {
for (int v : Tr[u]) if (v ^ fa && v ^ son[u]) dfs2(v, u, 0);
if ( ~ son[u]) dfs2(son[u], u, 1);
for (int v : Tr[u]) if (v ^ fa && v ^ son[u]) _for (i, dfn[v], dfn[v] + sz[v] - 1) upd(a[rnk[i]], 1); upd(a[u], 1), ans[u] = s[n - occ._Find_first() + 1];
if ( ! keep) _for (i, dfn[u], dfn[u] + sz[u] - 1) upd(a[rnk[i]], - 1);
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n; int u, v;
_for (i, 1, n) cin >> a[i];
_for (i, 2, n) cin >> u >> v, Tr[u].pb(v), Tr[v].pb(u); dfs1(1, 0), dfs2(1, 0, 0);
_for (i, 1, n) cout << ans[i] << " "; cout << "\n";
return 0;
}
CF1709E XOR Tree
显然可以通过改变 \(u\) 的点权,使得以 \(u\) 为 LCA 的路径不存在异或和为 \(0\) 的。
我们只需要判断 \(d_x \oplus d_y \oplus a_u\) 是否存在即可。
启发式合并 set 可以做到 \(O(n \log^2 n)\)。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define pb push_back
using namespace std;
const int N = 2e5 + 4;
int n, ans, a[N]; multiset<int> S[N]; vector<int> Tr[N];
void dfs(int u, int fa, int d) {
S[u].insert(d); int del = 0;
for (int v : Tr[u]) if (v ^ fa) {
dfs(v, u, d ^ a[v]);
if (S[u].size() < S[v].size()) swap(S[u], S[v]);
for (int i : S[v]) if (S[u].find(a[u] ^ i) != S[u].end()) del = 1;
for (int i : S[v]) S[u].insert(i);
} if (del) S[u].clear(), ans ++ ;
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
cin >> n; int u, v;
_for (i, 1, n) cin >> a[i];
_for (i, 2, n) cin >> u >> v, Tr[u].pb(v), Tr[v].pb(u); dfs(1, 0, a[1]), cout << ans << "\n";
return 0;
}
P4565 [CTSC2018] 暴力写挂
外面先套一个 DSU on Tree,现在你已知了 \(u\) 和 \(\text{lca}(u, v)\),需要支持:加入一个点 \(v\),查询所有点的 \(dep_v + dep'_{\text{lca}'(u, v)}\) 的最大值。
考虑对于第二棵树树剖。查询答案时,不断让 \(u\) 往上跳重链(设跳到的位置为 \(x\)),分两种情况维护信息:lca 在 \(x\) 到链顶这一段、lca 在 \(x\) 到链底这一段。分别用两棵 BIT 维护(需要对每一条重链建 BIT,只需支持单点修改,前缀 / 后缀求 \(\max\) 即可)。
复杂度 \(O(n \log^3 n)\)。由于三个 \(\log\) 都非常小(DSU on Tree、树剖、BIT),跑得很快。
有 \(O(n \log^2 n)\) 的点分治做法。
Code
#include <bits/stdc++.h>
#define _for(i, a, b) for (int i = (a); i <= (b); i ++ )
#define _all(i, a, b) for (int i = (a); i >= (b); i -- )
#define ll long long
#define PII pair<int, int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
using namespace std;
namespace IO {
const int LIM = 1e5;
static char buf[LIM], * p1 = buf, * p2 = buf, obuf[LIM], * p3 = obuf, cc[20];
#define gc() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, LIM, stdin), p1 == p2) ? EOF : * p1 ++ )
#define pc(x) (p3 - obuf < LIM) ? ( * p3 ++ = x) : (fwrite(obuf, p3 - obuf, 1, stdout), p3 = obuf, * p3 ++ = x)
inline int rd() {
int val = 0, sgn = 1; char ch = gc();
for ( ; ch < '0' || ch > '9'; ch = gc()) if (ch == '-') sgn = - 1;
for ( ; ch >= '0' && ch <= '9'; ch = gc()) val = (val << 3) + (val << 1) + (ch ^ 48); return (val * sgn);
} inline void wt(ll x, char c) {
if ( ! x) pc('0'); int len = 0;
if (x < 0) x = - x, pc('-');
while (x) cc[len ++ ] = x % 10 | '0', x /= 10;
while (len -- ) pc(cc[len]); pc(c);
}
} using namespace IO;
const int N = 4e5 + 4; const ll INF = (ll)1e18 + 5;
int n; ll ans = - INF;
inline void chkmax(ll & x, ll y) { x = x > y ? x : y; }
struct Fenwick1 {
int len; vector<ll> tr;
inline void init() { tr.assign(len + 4, - INF); }
inline int lowbit(int x) { return (x & ( - x)); }
inline void update(int x, ll k) { for (int i = x; i <= len; i += lowbit(i)) chkmax(tr[i], k); }
inline void clear(int x) { for (int i = x; i <= len && tr[i] ^ ( - INF); i += lowbit(i)) tr[i] = - INF; }
inline ll query(int x) { ll res = - INF; for (int i = x; i; i -= lowbit(i)) chkmax(res, tr[i]); return res; }
} BIT1[N];
struct Fenwick2 {
int len; vector<ll> tr;
inline void init() { tr.assign(len + 4, - INF); }
inline int lowbit(int x) { return (x & ( - x)); }
inline void update(int x, ll k) { for (int i = x; i; i -= lowbit(i)) chkmax(tr[i], k); }
inline void clear(int x) { for (int i = x; i && tr[i] ^ ( - INF); i -= lowbit(i)) tr[i] = - INF; }
inline ll query(int x) { ll res = - INF; for (int i = x; i <= len; i += lowbit(i)) chkmax(res, tr[i]); return res; }
} BIT2[N];
struct Tree {
int dfn_cnt, top[N], dfn[N], End[N], rnk[N], sz[N], son[N], fa[N], low[N]; ll dep[N]; vector<PII> Tr[N];
void dfs1(int u) {
sz[u] = 1, son[u] = - 1;
for (PII v : Tr[u]) if (v.fi ^ fa[u]) {
dep[v.fi] = dep[u] + v.se, fa[v.fi] = u, dfs1(v.fi), sz[u] += sz[v.fi];
if ( ! ~ son[u] || sz[son[u]] < sz[v.fi]) son[u] = v.fi;
}
} void dfs2(int u, int Top) {
rnk[dfn[u] = ++ dfn_cnt] = u, low[top[u] = Top] = u, BIT1[top[u]].len = BIT2[top[u]].len = dfn[u] - dfn[top[u]] + 1;
if ( ~ son[u]) dfs2(son[u], Top);
for (PII v : Tr[u]) if (v.fi ^ fa[u] && v.fi ^ son[u]) dfs2(v.fi, v.fi); End[u] = dfn_cnt;
} inline void update(int u, ll k) { while (u) BIT2[top[u]].update(dfn[u] - dfn[top[u]] + 1, k), BIT1[top[u]].update(dfn[u] - dfn[top[u]] + 1, k - dep[u]), u = fa[top[u]]; }
inline ll query(int u) {
ll res = - INF;
while (u) chkmax(res, max(BIT2[top[u]].query(dfn[u] - dfn[top[u]] + 1) - dep[u], BIT1[top[u]].query(dfn[u] - dfn[top[u]]))), u = fa[top[u]]; return res;
} inline void clear(int u) { while (u) BIT1[top[u]].clear(dfn[u] - dfn[top[u]] + 1), BIT2[top[u]].clear(dfn[u] - dfn[top[u]] + 1), u = fa[top[u]]; }
} T1, T2;
void gans(int u, int fa, bool keep) {
for (PII v : T1.Tr[u]) if (v.fi ^ fa && v.fi ^ T1.son[u]) gans(v.fi, u, 0);
if ( ~ T1.son[u]) gans(T1.son[u], u, 1); chkmax(ans, max(T2.query(T1.rnk[u]), T1.dep[u] - T2.dep[u])), T2.update(u, T1.dep[u]);
for (PII v : T1.Tr[u]) if (v.fi ^ fa && v.fi ^ T1.son[u]) {
_for (i, T1.dfn[v.fi], T1.End[v.fi]) chkmax(ans, T2.query(T1.rnk[i]) + T1.dep[T1.rnk[i]] - T1.dep[u]);
_for (i, T1.dfn[v.fi], T1.End[v.fi]) T2.update(T1.rnk[i], T1.dep[T1.rnk[i]]);
} if ( ! keep) _for (i, T1.dfn[u], T1.End[u]) T2.clear(T1.rnk[i]);
} signed main() {
ios :: sync_with_stdio(false), cin.tie(0), cout.tie(0);
n = rd(); int u, v, w;
_for (i, 2, n) u = rd(), v = rd(), w = rd(), T1.Tr[u].pb(mp(v, w)), T1.Tr[v].pb(mp(u, w)); T1.dfs1(1), T1.dfs2(1, 1);
_for (i, 2, n) u = rd(), v = rd(), w = rd(), T2.Tr[u].pb(mp(v, w)), T2.Tr[v].pb(mp(u, w)); T2.dfs1(1), T2.dfs2(1, 1);
_for (i, 1, n) BIT1[i].init(), BIT2[i].init(); gans(1, 0, 1), wt(n > 1 ? ans : 0, '\n');
return fwrite(obuf, p3 - obuf, 1, stdout), 0;
}

浙公网安备 33010602011771号