CF1633E Spanning Tree Queries
阿新 • • 發佈:2022-03-31
CF1633E
原題連結 ←Click it
題目大意:給定一個無向帶權圖,現有\(k\)次詢問,每次詢問一個\(x\),對每次詢問,邊權的價值是\(|w_i-x|\),求出最小生成樹的價值,最終的答案是對所有的詢問結果取異或。
解題思路:我們先來回憶一下kruskal演算法,先將所有邊權升序排序,然後依次選邊新增到集合中,可以發現如果每條邊權的相對大小如果不發生變化,那麼kruskal選的邊不變。然後再來研究一下絕對值函式(沒想到就是看起來這麼簡單的東西把我難倒了),我們驚訝地發現,邊權相對大小產生變化的地方只出現在兩條直線的交點位置,邊權斜率正負的改變只出現在與x軸的交點,那麼我們的想法就非常簡單了,我們將數軸按照這些交點切分,此時每一段區間的邊權相對大小都不發生改變,我們求出每一段區間某個值的(為了方便,這裡選擇最右端的值,也就是交點的x值)最小生成樹的價值,並統計生成樹中邊權斜率為正(或者為負)的數量。最後對每次詢問都可以在\(O(log)\)
參考程式碼:
int n, m; struct Edge { int u, v, w; }; vector<Edge> e; struct DSU { int N; vector<int> pr; DSU(int n) : N(n) { pr.resize(N + 1); for(int i = 0; i <= N; i ++) { pr[i] = i; } } int root(int x) { return pr[x] == x ? x : pr[x] = root(pr[x]); } void unite(int x, int y) { int X = root(x), Y = root(y); if(X == Y) { return ; } pr[X] = Y; } }; ll kruskal(int T, int &c) { //按照|w - T|排序,但是存在相等的情況要選w更小的,因為在交點左側|w - T|更小 sort(e.begin(), e.end(), [&] (const Edge &a, Edge &b) { if(abs(a.w - T) != abs(b.w - T)) return abs(a.w - T) < abs(b.w -T); else return a.w < b.w; }); DSU dsu(n); int cnt = 0; ll res = 0; for(auto eg : e) { int A = dsu.root(eg.u), B = dsu.root(eg.v); if(A == B) continue; dsu.unite(A, B); cnt ++; if(eg.w - T < 0) c ++; res += abs(eg.w - T); if(cnt == n - 1) { break; } } return res; } void solve() { cin >> n >> m; for(int i = 0; i < m; i ++) { int u, v, w; cin >> u >> v >> w; e.push_back({u, v, w}); } //定義了1e9為無窮 vector<int> pos(1, 1e9); for(int i = 0; i < m; i ++) { for(int j = i; j < m; j ++) { //新增交點到pos pos.push_back(e[i].w + e[j].w >> 1); } } sort(pos.begin(), pos.end()); //去重! pos.resize(unique(pos.begin(), pos.end()) - pos.begin()); vector<ll> dis, cnr; for(auto T : pos) { int c = 0; dis.push_back(kruskal(T, c)); cnr.push_back(c); } int p, k, a, b, c; cin >> p >> k >> a >> b >> c; vector<int> q(k); for(int i = 0; i < k; i ++) { if(i < p) { cin >> q[i]; } else { q[i] = (1ll * q[i - 1] * a + b) % c; } } ll res = 0; for(int i = 0; i < k; i ++) { int id = lower_bound(pos.begin(), pos.end(), q[i]) - pos.begin(); // 生成樹價值偏移量的計算 ll D = dis[id] - (cnr[id] * (pos[id] - q[i])) - (n - 1 - cnr[id]) * (q[i] - pos[id]); // cout << D << '\n'; res ^= D; } cout << res << '\n'; }