- 倍增
void dfs(int u, int fa) {
pa[u][0] = fa; dep[u] = dep[fa] + 1;
FOR (i, 1, SP) pa[u][i] = pa[pa[u][i - 1]][i - 1];
for (int& v: G[u]) {
if (v == fa) continue;
dfs(v, u);
}
}
int lca(int u, int v) {
if (dep[u] < dep[v]) swap(u, v);
int t = dep[u] - dep[v];
FOR (i, 0, SP) if (t & (1 << i)) u = pa[u][i];
FORD (i, SP - 1, -1) {
int uu = pa[u][i], vv = pa[v][i];
if (uu != vv) { u = uu; v = vv; }
}
return u == v ? u : pa[u][0];
}
- 最大流
struct E {
int to, cp;
E(int to, int cp): to(to), cp(cp) {}
};
struct Dinic {
static const int M = 1E5 * 5;
int m, s, t;
vector<E> edges;
vector<int> G[M];
int d[M];
int cur[M];
void init(int n, int s, int t) {
this->s = s; this->t = t;
for (int i = 0; i <= n; i++) G[i].clear();
edges.clear(); m = 0;
}
void addedge(int u, int v, int cap) {
edges.emplace_back(v, cap);
edges.emplace_back(u, 0);
G[u].push_back(m++);
G[v].push_back(m++);
}
bool BFS() {
memset(d, 0, sizeof d);
queue<int> Q;
Q.push(s); d[s] = 1;
while (!Q.empty()) {
int x = Q.front(); Q.pop();
for (int& i: G[x]) {
E &e = edges[i];
if (!d[e.to] && e.cp > 0) {
d[e.to] = d[x] + 1;
Q.push(e.to);
}
}
}
return d[t];
}
int DFS(int u, int cp) {
if (u == t || !cp) return cp;
int tmp = cp, f;
for (int& i = cur[u]; i < G[u].size(); i++) {
E& e = edges[G[u][i]];
if (d[u] + 1 == d[e.to]) {
f = DFS(e.to, min(cp, e.cp));
e.cp -= f;
edges[G[u][i] ^ 1].cp += f;
cp -= f;
if (!cp) break;
}
}
return tmp - cp;
}
int go() {
int flow = 0;
while (BFS()) {
memset(cur, 0, sizeof cur);
flow += DFS(s, INF);
}
return flow;
}
} DC;
- 费用流
struct E {
int from, to, cp, v;
E() {}
E(int f, int t, int cp, int v) : from(f), to(t), cp(cp), v(v) {}
};
struct MCMF {
int n, m, s, t;
vector<E> edges;
vector<int> G[M];
bool inq[M];
int d[M], p[M], a[M];
void init(int _n, int _s, int _t) {
n = _n; s = _s; t = _t;
FOR (i, 0, n + 1) G[i].clear();
edges.clear(); m = 0;
}
void addedge(int from, int to, int cap, int cost) {
edges.emplace_back(from, to, cap, cost);
edges.emplace_back(to, from, 0, -cost);
G[from].push_back(m++);
G[to].push_back(m++);
}
bool BellmanFord(int &flow, int &cost) {
FOR (i, 0, n + 1) d[i] = INF;
memset(inq, 0, sizeof inq);
d[s] = 0, a[s] = INF, inq[s] = true;
queue<int> Q; Q.push(s);
while (!Q.empty()) {
int u = Q.front(); Q.pop();
inq[u] = false;
for (int& idx: G[u]) {
E &e = edges[idx];
if (e.cp && d[e.to] > d[u] + e.v) {
d[e.to] = d[u] + e.v;
p[e.to] = idx;
a[e.to] = min(a[u], e.cp);
if (!inq[e.to]) {
Q.push(e.to);
inq[e.to] = true;
}
}
}
}
if (d[t] == INF) return false;
flow += a[t];
cost += a[t] * d[t];
int u = t;
while (u != s) {
edges[p[u]].cp -= a[t];
edges[p[u] ^ 1].cp += a[t];
u = edges[p[u]].from;
}
return true;
}
int go() {
int flow = 0, cost = 0;
while (BellmanFord(flow, cost));
return cost;
}
} MM;
- zkw 费用流(代码长度没有优势)
- 不允许有负权边
struct E {
int to, cp, v;
E() {}
E(int to, int cp, int v): to(to), cp(cp), v(v) {}
};
struct MCMF {
int n, m, s, t, cost, D;
vector<E> edges;
vector<int> G[N];
bool vis[N];
void init(int _n, int _s, int _t) {
n = _n; s = _s; t = _t;
FOR (i, 0, n + 1) G[i].clear();
edges.clear(); m = 0;
}
void addedge(int from, int to, int cap, int cost) {
edges.emplace_back(to, cap, cost);
edges.emplace_back(from, 0, -cost);
G[from].push_back(m++);
G[to].push_back(m++);
}
int aug(int u, int cp) {
if (u == t) {
cost += D * cp;
return cp;
}
vis[u] = true;
int tmp = cp;
for (int idx: G[u]) {
E& e = edges[idx];
if (e.cp && !e.v && !vis[e.to]) {
int f = aug(e.to, min(cp, e.cp));
e.cp -= f;
edges[idx ^ 1].cp += f;
cp -= f;
if (!cp) break;
}
}
return tmp - cp;
}
bool modlabel() {
int d = INF;
FOR (u, 0, n + 1)
if (vis[u])
for (int& idx: G[u]) {
E& e = edges[idx];
if (e.cp && !vis[e.to]) d = min(d, e.v);
}
if (d == INF) return false;
FOR (u, 0, n + 1)
if (vis[u])
for (int& idx: G[u]) {
edges[idx].v -= d;
edges[idx ^ 1].v += d;
}
D += d;
return true;
}
int go(int k) {
cost = D = 0;
int flow = 0;
while (true) {
memset(vis, 0, sizeof vis);
int t = aug(s, INF);
if (!t && !modlabel()) break;
flow += t;
}
return cost;
}
} MM;
- 带下界网络流:
- 无源汇:$u \rightarrow v$ 边容量为
$[l,r]$ ,连容量$r-l$ ,虚拟源点到$v$ 连$l$ ,$u$ 到虚拟汇点连$l$ 。 - 有源汇:为了让流能循环使用,连
$T \rightarrow S$ ,容量$\infty$ 。 - 最大流:跑完可行流后,加
$S' \rightarrow S$ ,$T \rightarrow T'$,最大流就是答案($T \rightarrow S$ 的流量自动退回去了,这一部分就是下界部分的流量)。 - 最小流:$T$ 到
$S$ 的那条边的实际流量,减去删掉那条边后$T$ 到$S$ 的最大流。 - 网上说可能会减成负的,还要有限地供应
$S$ 之后,再跑一遍$S$ 到$T$ 的。 - 费用流:必要的部分(下界以下的)不要钱,剩下的按照最大流。
- 无源汇:$u \rightarrow v$ 边容量为
int intersection(int x, int y, int xx, int yy) {
int t[4] = {lca(x, xx), lca(x, yy), lca(y, xx), lca(y, yy)};
sort(t, t + 4);
int r = lca(x, y), rr = lca(xx, yy);
if (dep[t[0]] < min(dep[r], dep[rr]) || dep[t[2]] < max(dep[r], dep[rr]))
return 0;
int tt = lca(t[2], t[3]);
int ret = 1 + dep[t[2]] + dep[t[3]] - dep[tt] * 2;
return ret;
}
int get_rt(int u) {
static int q[N], fa[N], sz[N], mx[N];
int p = 0, cur = -1;
q[p++] = u; fa[u] = -1;
while (++cur < p) {
u = q[cur]; mx[u] = 0; sz[u] = 1;
for (int& v: G[u])
if (!vis[v] && v != fa[u]) fa[q[p++] = v] = u;
}
FORD (i, p - 1, -1) {
u = q[i];
mx[u] = max(mx[u], p - sz[u]);
if (mx[u] * 2 <= p) return u;
sz[fa[u]] += sz[u];
mx[fa[u]] = max(mx[fa[u]], sz[u]);
}
assert(0);
}
void dfs(int u) {
u = get_rt(u);
vis[u] = true;
get_dep(u, -1, 0);
// ...
for (E& e: G[u]) {
int v = e.to;
if (vis[v]) continue;
// ...
dfs(v);
}
}
- 动态点分治
const int N = 15E4 + 100, INF = 1E9;
struct E {
int to, d;
};
vector<E> G[N];
int n, Q, w[N];
LL A, ans;
bool vis[N];
int sz[N];
int get_rt(int u) {
static int q[N], fa[N], sz[N], mx[N];
int p = 0, cur = -1;
q[p++] = u; fa[u] = -1;
while (++cur < p) {
u = q[cur]; mx[u] = 0; sz[u] = 1;
for (int& v: G[u])
if (!vis[v] && v != fa[u]) fa[q[p++] = v] = u;
}
FORD (i, p - 1, -1) {
u = q[i];
mx[u] = max(mx[u], p - sz[u]);
if (mx[u] * 2 <= p) return u;
sz[fa[u]] += sz[u];
mx[fa[u]] = max(mx[fa[u]], sz[u]);
}
assert(0);
}
int dep[N], md[N];
void get_dep(int u, int fa, int d) {
dep[u] = d; md[u] = 0;
for (E& e: G[u]) {
int v = e.to;
if (vis[v] || v == fa) continue;
get_dep(v, u, d + e.d);
md[u] = max(md[u], md[v] + 1);
}
}
struct P {
int w;
LL s;
};
using VP = vector<P>;
struct R {
VP *rt, *rt2;
int dep;
};
VP pool[N << 1], *pit = pool;
vector<R> tr[N];
void go(int u, int fa, VP* rt, VP* rt2) {
tr[u].push_back({rt, rt2, dep[u]});
for (E& e: G[u]) {
int v = e.to;
if (v == fa || vis[v]) continue;
go(v, u, rt, rt2);
}
}
void dfs(int u) {
u = get_rt(u);
vis[u] = true;
get_dep(u, -1, 0);
VP* rt = pit++; tr[u].push_back({rt, nullptr, 0});
for (E& e: G[u]) {
int v = e.to;
if (vis[v]) continue;
go(v, u, rt, pit++);
dfs(v);
}
}
bool cmp(const P& a, const P& b) { return a.w < b.w; }
LL query(VP& p, int d, int l, int r) {
l = lower_bound(p.begin(), p.end(), P{l, -1}, cmp) - p.begin();
r = upper_bound(p.begin(), p.end(), P{r, -1}, cmp) - p.begin() - 1;
return p[r].s - p[l - 1].s + 1LL * (r - l + 1) * d;
}
int main() {
cin >> n >> Q >> A;
FOR (i, 1, n + 1) scanf("%d", &w[i]);
FOR (_, 1, n) {
int u, v, d; scanf("%d%d%d", &u, &v, &d);
G[u].push_back({v, d}); G[v].push_back({u, d});
}
dfs(1);
FOR (i, 1, n + 1)
for (R& x: tr[i]) {
x.rt->push_back({w[i], x.dep});
if (x.rt2) x.rt2->push_back({w[i], x.dep});
}
FOR (it, pool, pit) {
it->push_back({-INF, 0});
sort(it->begin(), it->end(), cmp);
FOR (i, 1, it->size())
(*it)[i].s += (*it)[i - 1].s;
}
while (Q--) {
int u; LL a, b; scanf("%d%lld%lld", &u, &a, &b);
a = (a + ans) % A; b = (b + ans) % A;
int l = min(a, b), r = max(a, b);
ans = 0;
for (R& x: tr[u]) {
ans += query(*(x.rt), x.dep, l, r);
if (x.rt2) ans -= query(*(x.rt2), x.dep, l, r);
}
printf("%lld\n", ans);
}
}
- 初始化需要清空
clk
- 使用
hld::predfs(1, 1); hld::dfs(1, 1);
int fa[N], dep[N], idx[N], out[N], ridx[N];
namespace hld {
int sz[N], son[N], top[N], clk;
void predfs(int u, int d) {
dep[u] = d; sz[u] = 1;
int& maxs = son[u] = -1;
for (int& v: G[u]) {
if (v == fa[u]) continue;
fa[v] = u;
predfs(v, d + 1);
sz[u] += sz[v];
if (maxs == -1 || sz[v] > sz[maxs]) maxs = v;
}
}
void dfs(int u, int tp) {
top[u] = tp; idx[u] = ++clk; ridx[clk] = u;
if (son[u] != -1) dfs(son[u], tp);
for (int& v: G[u])
if (v != fa[u] && v != son[u]) dfs(v, v);
out[u] = clk;
}
template<typename T>
int go(int u, int v, T&& f = [](int, int) {}) {
int uu = top[u], vv = top[v];
while (uu != vv) {
if (dep[uu] < dep[vv]) { swap(uu, vv); swap(u, v); }
f(idx[uu], idx[u]);
u = fa[uu]; uu = top[u];
}
if (dep[u] < dep[v]) swap(u, v);
// choose one
// f(idx[v], idx[u]);
// if (u != v) f(idx[v] + 1, idx[u]);
return v;
}
int up(int u, int d) {
while (d) {
if (dep[u] - dep[top[u]] < d) {
d -= dep[u] - dep[top[u]];
u = top[u];
} else return ridx[idx[u] - d];
u = fa[u]; --d;
}
return u;
}
int finds(int u, int rt) { // 找 u 在 rt 的哪个儿子的子树中
while (top[u] != top[rt]) {
u = top[u];
if (fa[u] == rt) return u;
u = fa[u];
}
return ridx[idx[rt] + 1];
}
}
- 最小覆盖数 = 最大匹配数
- 最大独立集 = 顶点数 - 二分图匹配数
- DAG 最小路径覆盖数 = 结点数 - 拆点后二分图最大匹配数
struct MaxMatch {
int n;
vector<int> G[N];
int vis[N], left[N], clk;
void init(int n) {
this->n = n;
FOR (i, 0, n + 1) G[i].clear();
memset(left, -1, sizeof left);
memset(vis, -1, sizeof vis);
}
bool dfs(int u) {
for (int v: G[u])
if (vis[v] != clk) {
vis[v] = clk;
if (left[v] == -1 || dfs(left[v])) {
left[v] = u;
return true;
}
}
return false;
}
int match() {
int ret = 0;
for (clk = 0; clk <= n; ++clk)
if (dfs(clk)) ++ret;
return ret;
}
} MM;
- 二分图最大权完美匹配 KM ($O(n^3)$)
namespace R {
const int M = 400 + 5;
const int INF = 2E9;
int n;
int w[M][M], kx[M], ky[M], py[M], vy[M], slk[M], pre[M];
LL KM() {
FOR (i, 1, n + 1)
FOR (j, 1, n + 1)
kx[i] = max(kx[i], w[i][j]);
FOR (i, 1, n + 1) {
fill(vy, vy + n + 1, 0);
fill(slk, slk + n + 1, INF);
fill(pre, pre + n + 1, 0);
int k = 0, p = -1;
for (py[k = 0] = i; py[k]; k = p) {
int d = INF;
vy[k] = 1;
int x = py[k];
FOR (j, 1, n + 1)
if (!vy[j]) {
int t = kx[x] + ky[j] - w[x][j];
if (t < slk[j]) { slk[j] = t; pre[j] = k; }
if (slk[j] < d) { d = slk[j]; p = j; }
}
FOR (j, 0, n + 1)
if (vy[j]) { kx[py[j]] -= d; ky[j] += d; }
else slk[j] -= d;
}
for (; k; k = pre[k]) py[k] = py[pre[k]];
}
LL ans = 0;
FOR (i, 1, n + 1) ans += kx[i] + ky[i];
return ans;
}
}
void go(vector<int>& V, int& k) {
int u = V[k]; f[u] = 0;
dbg(u, k);
for (auto& e: G[u]) {
int v = e.to;
if (v == pa[u][0]) continue;
while (k + 1 < V.size()) {
int to = V[k + 1];
if (in[to] <= out[v]) {
go(V, ++k);
if (key[to]) f[u] += w[to];
else f[u] += min(f[to], (LL)w[to]);
} else break;
}
}
dbg(u, f[u]);
}
inline bool cmp(int a, int b) { return in[a] < in[b]; }
LL solve(vector<int>& V) {
static vector<int> a; a.clear();
for (int& x: V) a.push_back(x);
sort(a.begin(), a.end(), cmp);
FOR (i, 1, a.size())
a.push_back(lca(a[i], a[i - 1]));
a.push_back(1);
sort(a.begin(), a.end(), cmp);
a.erase(unique(a.begin(), a.end()), a.end());
dbg(a);
int tmp; go(a, tmp = 0);
return f[1];
}
int S[N << 1], top;
Edge edges[N << 1];
set<int> G[N];
void DFS(int u) {
S[top++] = u;
for (int eid: G[u]) {
int v = edges[eid].get_other(u);
G[u].erase(eid);
G[v].erase(eid);
DFS(v);
return;
}
}
void fleury(int start) {
int u = start;
top = 0; path.clear();
S[top++] = u;
while (top) {
u = S[--top];
if (!G[u].empty())
DFS(u);
else path.push_back(u);
}
}
int n, m;
vector<int> G[N], rG[N], vs;
int used[N], cmp[N];
void add_edge(int from, int to) {
G[from].push_back(to);
rG[to].push_back(from);
}
void dfs(int v) {
used[v] = true;
for (int u: G[v]) {
if (!used[u])
dfs(u);
}
vs.push_back(v);
}
void rdfs(int v, int k) {
used[v] = true;
cmp[v] = k;
for (int u: rG[v])
if (!used[u])
rdfs(u, k);
}
int scc() {
memset(used, 0, sizeof(used));
vs.clear();
for (int v = 0; v < n; ++v)
if (!used[v]) dfs(v);
memset(used, 0, sizeof(used));
int k = 0;
for (int i = (int) vs.size() - 1; i >= 0; --i)
if (!used[vs[i]]) rdfs(vs[i], k++);
return k;
}
int main() {
cin >> n >> m;
n *= 2;
for (int i = 0; i < m; ++i) {
int a, b; cin >> a >> b;
add_edge(a - 1, (b - 1) ^ 1);
add_edge(b - 1, (a - 1) ^ 1);
}
scc();
for (int i = 0; i < n; i += 2) {
if (cmp[i] == cmp[i + 1]) {
puts("NIE");
return 0;
}
}
for (int i = 0; i < n; i += 2) {
if (cmp[i] > cmp[i + 1]) printf("%d\n", i + 1);
else printf("%d\n", i + 2);
}
}
vector<int> toporder(int n) {
vector<int> orders;
queue<int> q;
for (int i = 0; i < n; i++)
if (!deg[i]) {
q.push(i);
orders.push_back(i);
}
while (!q.empty()) {
int u = q.front(); q.pop();
for (int v: G[u])
if (!--deg[v]) {
q.push(v);
orders.push_back(v);
}
}
return orders;
}
带花树。复杂度
int n;
vector<int> G[N];
int fa[N], mt[N], pre[N], mk[N];
int lca_clk, lca_mk[N];
pair<int, int> ce[N];
void connect(int u, int v) {
mt[u] = v;
mt[v] = u;
}
int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
void flip(int s, int u) {
if (s == u) return;
if (mk[u] == 2) {
int v1 = ce[u].first, v2 = ce[u].second;
flip(mt[u], v1);
flip(s, v2);
connect(v1, v2);
} else {
flip(s, pre[mt[u]]);
connect(pre[mt[u]], mt[u]);
}
}
int get_lca(int u, int v) {
lca_clk++;
for (u = find(u), v = find(v); ; u = find(pre[u]), v = find(pre[v])) {
if (u && lca_mk[u] == lca_clk) return u;
lca_mk[u] = lca_clk;
if (v && lca_mk[v] == lca_clk) return v;
lca_mk[v] = lca_clk;
}
}
void access(int u, int p, const pair<int, int>& c, vector<int>& q) {
for (u = find(u); u != p; u = find(pre[u])) {
if (mk[u] == 2) {
ce[u] = c;
q.push_back(u);
}
fa[find(u)] = find(p);
}
}
bool aug(int s) {
fill(mk, mk + n + 1, 0);
fill(pre, pre + n + 1, 0);
iota(fa, fa + n + 1, 0);
vector<int> q = {s};
mk[s] = 1;
int t = 0;
for (int t = 0; t < (int) q.size(); ++t) {
// q size can be changed
int u = q[t];
for (int &v: G[u]) {
if (find(v) == find(u)) continue;
if (!mk[v] && !mt[v]) {
flip(s, u);
connect(u, v);
return true;
} else if (!mk[v]) {
int w = mt[v];
mk[v] = 2; mk[w] = 1;
pre[w] = v; pre[v] = u;
q.push_back(w);
} else if (mk[find(v)] == 1) {
int p = get_lca(u, v);
access(u, p, {u, v}, q);
access(v, p, {v, u}, q);
}
}
}
return false;
}
int match() {
fill(mt + 1, mt + n + 1, 0);
lca_clk = 0;
int ans = 0;
FOR (i, 1, n + 1)
if (!mt[i]) ans += aug(i);
return ans;
}
int main() {
int m; cin >> n >> m;
while (m--) {
int u, v; scanf("%d%d", &u, &v);
G[u].push_back(v); G[v].push_back(u);
}
printf("%d\n", match());
FOR (i, 1, n + 1) printf("%d%c", mt[i], i == _i - 1 ? '\n' : ' ');
return 0;
}
- 判断割点
- 注意原图可能不连通
int dfn[N], low[N], clk;
void init() { clk = 0; memset(dfn, 0, sizeof dfn); }
void tarjan(int u, int fa) {
low[u] = dfn[u] = ++clk;
int cc = fa != -1;
for (int& v: G[u]) {
if (v == fa) continue;
if (!dfn[v]) {
tarjan(v, u);
low[u] = min(low[u], low[v]);
cc += low[v] >= dfn[u];
} else low[u] = min(low[u], dfn[v]);
}
if (cc > 1) // ...
}
- 注意原图不连通和重边
int dfn[N], low[N], clk;
void init() { memset(dfn, 0, sizeof dfn); clk = 0; }
void tarjan(int u, int fa) {
low[u] = dfn[u] = ++clk;
int _fst = 0;
for (E& e: G[u]) {
int v = e.to; if (v == fa && ++_fst == 1) continue;
if (!dfn[v]) {
tarjan(v, u);
if (low[v] > dfn[u]) // ...
low[u] = min(low[u], low[v]);
} else low[u] = min(low[u], dfn[v]);
}
}
int low[N], dfn[N], clk, B, bl[N];
vector<int> bcc[N];
void init() { B = clk = 0; memset(dfn, 0, sizeof dfn); }
void tarjan(int u) {
static int st[N], p;
static bool in[N];
dfn[u] = low[u] = ++clk;
st[p++] = u; in[u] = true;
for (int& v: G[u]) {
if (!dfn[v]) {
tarjan(v);
low[u] = min(low[u], low[v]);
} else if (in[v]) low[u] = min(low[u], dfn[v]);
}
if (dfn[u] == low[u]) {
while (1) {
int x = st[--p]; in[x] = false;
bl[x] = B; bcc[B].push_back(x);
if (x == u) break;
}
++B;
}
}
- 数组开两倍
- 一条边也被计入点双了(适合拿来建圆方树),可以用 点数 <= 边数 过滤
struct E { int to, nxt; } e[N];
int hd[N], ecnt;
void addedge(int u, int v) {
e[ecnt] = {v, hd[u]};
hd[u] = ecnt++;
}
int low[N], dfn[N], clk, B, bno[N];
vector<int> bc[N], be[N];
bool vise[N];
void init() {
memset(vise, 0, sizeof vise);
memset(hd, -1, sizeof hd);
memset(dfn, 0, sizeof dfn);
memset(bno, -1, sizeof bno);
B = clk = ecnt = 0;
}
void tarjan(int u, int feid) {
static int st[N], p;
static auto add = [&](int x) {
if (bno[x] != B) { bno[x] = B; bc[B].push_back(x); }
};
low[u] = dfn[u] = ++clk;
for (int i = hd[u]; ~i; i = e[i].nxt) {
if ((feid ^ i) == 1) continue;
if (!vise[i]) { st[p++] = i; vise[i] = vise[i ^ 1] = true; }
int v = e[i].to;
if (!dfn[v]) {
tarjan(v, i);
low[u] = min(low[u], low[v]);
if (low[v] >= dfn[u]) {
bc[B].clear(); be[B].clear();
while (1) {
int eid = st[--p];
add(e[eid].to); add(e[eid ^ 1].to);
be[B].push_back(eid);
if ((eid ^ i) <= 1) break;
}
++B;
}
} else low[u] = min(low[u], dfn[v]);
}
}
- 从仙人掌建圆方树
- N 至少边数 × 2
vector<int> G[N];
int nn;
struct E { int to, nxt; };
namespace C {
E e[N * 2];
int hd[N], ecnt;
void addedge(int u, int v) {
e[ecnt] = {v, hd[u]};
hd[u] = ecnt++;
}
int idx[N], clk, fa[N];
bool ring[N];
void init() { ecnt = 0; memset(hd, -1, sizeof hd); clk = 0; }
void dfs(int u, int feid) {
idx[u] = ++clk;
for (int i = hd[u]; ~i; i = e[i].nxt) {
if ((i ^ feid) == 1) continue;
int v = e[i].to;
if (!idx[v]) {
fa[v] = u; ring[u] = false;
dfs(v, i);
if (!ring[u]) { G[u].push_back(v); G[v].push_back(u); }
} else if (idx[v] < idx[u]) {
++nn;
G[nn].push_back(v); G[v].push_back(nn); // 强行把环的根放在最前面
for (int x = u; x != v; x = fa[x]) {
ring[x] = true;
G[nn].push_back(x); G[x].push_back(nn);
}
ring[v] = true;
}
}
}
}
会篡改边。
vector<E> edges;
int in[N], id[N], pre[N], vis[N];
// a copy of n is needed
LL zl_tree(int rt, int n) {
LL ans = 0;
int v, _n = n;
while (1) {
fill(in, in + n, INF);
for (E &e: edges) {
if (e.u != e.v && e.w < in[e.v]) {
pre[e.v] = e.u;
in[e.v] = e.w;
}
}
FOR (i, 0, n) if (i != rt && in[i] == INF) return -1;
int tn = 0;
fill(id, id + _n, -1); fill(vis, vis + _n, -1);
in[rt] = 0;
FOR (i, 0, n) {
ans += in[v = i];
while (vis[v] != i && id[v] == -1 && v != rt) {
vis[v] = i; v = pre[v];
}
if (v != rt && id[v] == -1) {
for (int u = pre[v]; u != v; u = pre[u]) id[u] = tn;
id[v] = tn++;
}
}
if (tn == 0) break;
FOR (i, 0, n) if (id[i] == -1) id[i] = tn++;
for (int i = 0; i < (int) edges.size(); ) {
auto &e = edges[i];
v = e.v;
e.u = id[e.u]; e.v = id[e.v];
if (e.u != e.v) { e.w -= in[v]; i++; }
else { swap(e, edges.back()); edges.pop_back(); }
}
n = tn; rt = id[rt];
}
return ans;
}
一个系统
若要使得所有量两两的值最接近,源点到各点的距离初始成
若要使得某一变量与其他变量的差尽可能大,则源点到各点距离初始化成
考虑这样一个四元环,将答案统计在度数最大的点
枚举完
任何一个点,与其直接相连的度数大于等于它的点最多只有
LL cycle4() {
LL ans = 0;
iota(kth, kth + n + 1, 0);
sort(kth, kth + n, [&](int x, int y) { return deg[x] < deg[y]; });
FOR (i, 1, n + 1) rk[kth[i]] = i;
FOR (u, 1, n + 1)
for (int v: G[u])
if (rk[v] > rk[u]) key[u].push_back(v);
FOR (u, 1, n + 1) {
for (int v: G[u])
for (int w: key[v])
if (rk[w] > rk[u]) ans += cnt[w]++;
for (int v: G[u])
for (int w: key[v])
if (rk[w] > rk[u]) --cnt[w];
}
return ans;
}
将点分成度入小于
对于每条无向边
int cycle3() {
int ans = 0;
for (E &e: edges) { deg[e.u]++; deg[e.v]++; }
for (E &e: edges) {
if (deg[e.u] < deg[e.v] || (deg[e.u] == deg[e.v] && e.u < e.v))
G[e.u].push_back(e.v);
else G[e.v].push_back(e.u);
}
FOR (x, 1, n + 1) {
for (int y: G[x]) p[y] = x;
for (int y: G[x]) for (int z: G[y]) if (p[z] == x) ans++;
}
return ans;
}
-
semi[x]
半必经点(就是$x$ 的祖先$z$ 中,能不经过$z$ 和$x$ 之间的树上的点而到达$x$ 的点中深度最小的) -
idom[x]
最近必经点(就是深度最大的根到$x$ 的必经点)
vector<int> G[N], rG[N];
vector<int> dt[N];
namespace tl{
int fa[N], idx[N], clk, ridx[N];
int c[N], best[N], semi[N], idom[N];
void init(int n) {
clk = 0;
fill(c, c + n + 1, -1);
FOR (i, 1, n + 1) dt[i].clear();
FOR (i, 1, n + 1) semi[i] = best[i] = i;
fill(idx, idx + n + 1, 0);
}
void dfs(int u) {
idx[u] = ++clk; ridx[clk] = u;
for (int& v: G[u]) if (!idx[v]) { fa[v] = u; dfs(v); }
}
int fix(int x) {
if (c[x] == -1) return x;
int &f = c[x], rt = fix(f);
if (idx[semi[best[x]]] > idx[semi[best[f]]]) best[x] = best[f];
return f = rt;
}
void go(int rt) {
dfs(rt);
FORD (i, clk, 1) {
int x = ridx[i], mn = clk + 1;
for (int& u: rG[x]) {
if (!idx[u]) continue; // 可能不能到达所有点
fix(u); mn = min(mn, idx[semi[best[u]]]);
}
c[x] = fa[x];
dt[semi[x] = ridx[mn]].push_back(x);
x = ridx[i - 1];
for (int& u: dt[x]) {
fix(u);
if (semi[best[u]] != x) idom[u] = best[u];
else idom[u] = x;
}
dt[x].clear();
}
FOR (i, 2, clk + 1) {
int u = ridx[i];
if (idom[u] != semi[u]) idom[u] = idom[idom[u]];
dt[idom[u]].push_back(u);
}
}
}