題面
給你兩個序列\(A, B\), \(\forall u, v(u \not = v)\)之間邊的權值為\(a_ua_v+b_ub_v\)。求最小生成樹的邊權和。
原題目
editorial
樸素的想法
考慮類似題目的做法,考慮每一次尋找最小的然後加入。發現這種思想和Boruvka比較相似。於是我們考慮Boruvka的方式來做。
對現有的連通塊的基礎上考慮:我們可以將這條新的邊放在連通塊編號在當前連通塊前面的,也可以放在連通塊編號在當前編號後面的(假設對當前的連通塊標編號)。那麼我們需要對這兩部分分開計算。那麼就可以轉化問題了。
轉化問題
問題變成了這樣:
我們要維護一個資料結構,支援每次向集合\(S\)加入一個數對\((a, b)\), 並詢問時給出\((x, y)\), 求\(\min \limits_{(a, b) \in S} (ax+by)\)
這個問題在maspy的題解裡面寫的是用凸包或CHT
。
但是我不會geo的任何東西,於是轉化成ds。
當\(b\not = 0\)時,相當不人類智慧的考慮到\(ax+by=y(\frac{x}{y}a+b)\),看作時某個位置的一次函式最值乘上一個常數,發現可以直接李超線段樹維護。
note
注意一下0的情況。至少我還需要除了線段樹要計所有加入a的max,所有加入a的min,所有加入且b為0的a的max,所有加入且b為0的a的min,然後在做。詳細的見程式碼(有點醜)
code
using db = double;
inline constexpr static db eps = 1e-10;
constexpr int N = 5e4 + 5;
int n, fa[N], dsu_con_cnt = 0;
ll ans = 0, val[N], a[N], b[N];
db pos[N];
vector<int> cons[N];
pii rcs[N];
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
bool merge(int u, int v) { auto du = find(u), dv = find(v); if (du != dv) return fa[du] = dv, --dsu_con_cnt, true; else return false; }
struct Line {
ll k, b;
Line(ll _k = inf<ll>, ll _b = 0) : k(_k), b(_b) { }
Line& operator=(const Line&x) { k = x.k, b = x.b; return *this; }
};
struct segnode {
Line l1, l2;
} seg[N << 2];
# define lson index << 1
# define rson index << 1 | 1
inline db calc(Line cur, db pos) {
return static_cast<db>(cur.k) * pos + static_cast<db>(cur.b);
}
void build(int index, int l, int r) {
if (l > r) return ;
seg[index].l1 = seg[index].l2 = Line();
if (l == r) return ;
int mid = (l + r) >> 1;
build(lson, l, mid);
build(rson, mid + 1, r);
}
void change1(int index, int l, int r, Line x) {
if (l > r) return ;
int mid = (l + r) >> 1;
if (seg[index].l1.k == inf<ll> || calc(seg[index].l1, pos[mid]) < calc(x, pos[mid])) swap(seg[index].l1, x);
if (x.k == inf<ll> || l == r) return ;
if (calc(seg[index].l1, pos[l]) < calc(x, pos[l])) change1(lson, l, mid, x);
if (calc(seg[index].l1, pos[r]) < calc(x, pos[r])) change1(rson, mid + 1, r, x);
}
void query1(int index, int l, int r, int qpos, Line &cur) {
if (l > r) return ;
int mid = (l + r) >> 1;
if (seg[index].l1.k == inf<ll>) return;
if (cur.k == inf<ll> || calc(cur, pos[qpos]) < calc(seg[index].l1, pos[qpos])) cur = seg[index].l1;
if (l == r) return ;
if (qpos <= mid) query1(lson, l, mid, qpos, cur);
else query1(rson, mid + 1, r, qpos, cur);
}
void change2(int index, int l, int r, Line x) {
if (l > r)return ;
int mid = (l + r) >> 1;
if (seg[index].l2.k == inf<ll> || calc(seg[index].l2, pos[mid]) > calc(x, pos[mid])) swap(seg[index].l2, x);
if (x.k == inf<ll> || l == r) return ;
if (calc(seg[index].l2, pos[l]) > calc(x, pos[l])) change2(lson, l, mid, x);
if (calc(seg[index].l2, pos[r]) > calc(x, pos[r])) change2(rson, mid + 1, r, x);
}
void query2(int index, int l, int r, int qpos, Line &cur) {
if (l > r) return ;
int mid = (l + r) >> 1;
if (seg[index].l2.k == inf<ll>) return;
if (cur.k == inf<ll> || calc(cur, pos[qpos]) > calc(seg[index].l2, pos[qpos])) cur = seg[index].l2;
if (l == r) return ;
if (qpos <= mid) query2(lson, l, mid, qpos, cur);
else query2(rson, mid + 1, r, qpos, cur);
}
struct segtree_cht {
int sz, tot;
bool seg1_added;
map<pair<ll, ll>, int> idx;
pair<ll, int> amx, amn, amx_zero, amn_zero;
segtree_cht() { amx = mkp(-inf<ll>, -1), amn = mkp(inf<ll>, -1), amx_zero = mkp(-inf<ll>, -1), amn_zero = mkp(inf<ll>, -1); seg1_added = false; }
void rsz(int _sz) { sz = _sz; }
void work(ll *X, ll *Y) {
rep(i, 1, n) idx[mkp(X[i], Y[i])] = i;
rep(i, 1, n) if (Y[i]) ::pos[++tot] = db(X[i]) / db(Y[i]);
if (tot) {
sort(pos + 1, pos + tot + 1);
tot = unique(pos + 1, pos + tot + 1) - pos - 1;
}
}
void rst() { seg1_added = false; build(1, 1, tot); }
void add(ll a, ll b) {
auto _id = idx[mkp(a, b)];
chkmax(amx, mkp(a, _id)), chkmin(amn, mkp(a, _id));
if (!b) {
auto _id = idx[mkp(a, b)];
chkmax(amx_zero, mkp(a, _id)), chkmin(amn_zero, mkp(a, _id));
}
change1(1, 1, tot, Line(a, b));
seg1_added = true;
change2(1, 1, tot, Line(a, b));
}
ll query_max(ll x, ll y) {
if (!seg1_added) return -inf<ll>;
if (!y) {
if (x >= 0) return x * amx.first;
else return x * amn.first;
}
int p = lower_bound(pos + 1, pos + tot + 1, db(x) / y) - pos;
Line tans;
tans.k = inf<ll>;
if (y >= 0) query1(1, 1, tot, p, tans);
else query2(1, 1, tot, p, tans);
return tans.k * x + tans.b * y;
}
pair<ll, int> query_max_with_id(ll x, ll y) {
if (!seg1_added) return mkp(-inf<ll>, -1);
if (!y) {
if (x >= 0) return mkp(x * amx.first, amx.second);
else return mkp(x * amn.first, amn.second);
}
int p = lower_bound(pos + 1, pos + tot + 1, db(x) / y) - pos;
Line tans;
tans.k = inf<ll>;
if (y > 0) query1(1, 1, tot, p, tans);
else query2(1, 1, tot, p, tans);
pair<ll, int> curans = mkp(tans.k * x + tans.b * y, idx[mkp(tans.k, tans.b)]);
if (x >= 0) {
if (amx_zero.first != -inf<ll>) chkmax(curans, mkp(x * amx_zero.first, amx_zero.second));
} else {
if (amn_zero.first != inf<ll>) chkmax(curans, mkp(x * amn_zero.first, amn_zero.second));
}
return curans;
}
ll query_min(ll x, ll y) {
return -query_max(-x, -y);
}
pair<ll, int> query_min_with_id(ll x, ll y) {
auto ret = query_max_with_id(-x, -y);
return mkp(-ret.first, ret.second);
}
};
segtree_cht T;
signed main() {
read(n); T.rsz(n);
iota(fa + 1, fa + n + 1, 1);
dsu_con_cnt = n;
rep(i, 1, n) read(a[i]);
rep(i, 1, n) read(b[i]);
T.work(a, b);
while(dsu_con_cnt > 1) {
bool flag = false;
rep(i, 1, n) cons[i].clear();
rep(i, 1, n) cons[find(i)].eb(i);
fill(val + 1, val + n + 1, inf<ll>);
fill(rcs + 1, rcs + n + 1, mkp(0, 0));
T.rst();
rep(u, 1, n) {
for(auto v : cons[u]) {
auto [tv, w] = T.query_min_with_id(a[v], b[v]);
// dbg(u, v, w, tv);
if (val[u] > tv) val[u] = tv, rcs[u] = mkp(v, w);
}
for (auto v : cons[u])
T.add(a[v], b[v]);
}
T.rst();
per(u, n, 1) {
for(auto v : cons[u]) {
auto [tv, w] = T.query_min_with_id(a[v], b[v]);
// dbg(u, v, w, tv);
if (val[u] > tv) val[u] = tv, rcs[u] = mkp(v, w);
}
for (auto v : cons[u])
T.add(a[v], b[v]);
}
rep(i, 1, n) {
auto [u, v] = rcs[i];
if (!u) continue;
if (merge(u, v)) {
flag = true;
ans += val[i];
}
}
if (!flag) break;
}
writeln(ans);
#if defined(LOCAL) && !defined(CPH)
std::cerr << "Spend Time : " << clock() * 1. / CLOCKS_PER_SEC * 1e3 << " ms \n";
#endif
return 0;
}
後來發現我的想法和maspy的基本相同,而李超樹寫法好像比maspy的CHT板子跑的快!