CF1633E Spanning Tree Queries
作者:互联网
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';
}
标签:CF1633E,int,边权,Tree,pos,++,vector,Spanning,id 来源: https://www.cnblogs.com/Muly/p/16080257.html