有一个n个点A+B条边的无向连通图,有一变量x,每条边的权值都是一个关于x的简单多项式,其中有A条边的权值是k+x,另外B条边的权值是k-x,如果只保留权值形如k+x的边,那么这个图仍是一个连通图,如果只保留权值形如k-x的边,这个图也依然是一个连通图。
给出q组询问,每组询问给出x的值,问此时这个无向连通图的最小生成树权值是多少。
1<=n,q<=100000 , n-1<=A,B<=200000, 0<=k<=10^9 , -10^9<=v<=10^9
这题考场就差那么一点点就想出来了。
题目都告诉你了A、B两个都可以生成一颗最小生成树。
那一定是对两个都建最小生成树,然后把另外一棵的边从小到大依次插入到这棵中,因为如果是其它边更优,那么这棵最小生成树一定会包含这条边,解释不清,需要感受。
插入到这棵树中,会形成一个环,那么把环上最大的边删掉,如果这条边是k1+x,当前这条是k2-x,那么当v>=(k2-k1)/2,k2-x这条边就会在最优的最小生成树上,答案就可以变小。
所以对所有(k2-k1)/2排个序,读入v二分一下即可算出答案。
加边删边lct维护。
Code:
#include
#include
#include
#define ll long long
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
#define fo(i, x, y) for(int i = x; i <= y; i ++)
using namespace std;
const int N = 3e5 + 50;
int z[N], te;
int fa[N], t[N][2], pf[N], rev[N], dd[N], mx[N];
int lr(int x) {return t[fa[x]][1] == x;}
void fan(int x) {if(x) swap(t[x][0], t[x][1]), rev[x] ^= 1;}
void down(int x) {if(x && rev[x]) fan(t[x][0]), fan(t[x][1]), rev[x] = 0;}
void update(int x) {
if(!x) return;
mx[x] = x;
mx[x] = z[mx[t[x][0]]] > z[mx[x]] ? mx[t[x][0]] : mx[x];
mx[x] = z[mx[t[x][1]]] > z[mx[x]] ? mx[t[x][1]] : mx[x];
}
void xc(int x) {
for(; x; x = fa[x]) dd[++ dd[0]] = x;
for(; dd[0]; dd[0] --) down(dd[dd[0]]);
}
void rotate(int x) {
int y = fa[x], k = lr(x);
t[y][k] = t[x][!k]; if(t[x][!k]) fa[t[x][!k]] = y;
fa[x] = fa[y]; if(fa[y]) t[fa[y]][lr(y)] = x;
t[x][!k] = y; fa[y] = x; pf[x] = pf[y];
update(y); update(x);
}
void splay(int x, int y) {
xc(x);
while(fa[x] != y) {
if(fa[fa[x]] != y)
if(lr(x) == lr(fa[x])) rotate(fa[x]); else rotate(x);
rotate(x);
}
}
void access(int x) {
for(int y = 0; x; update(x), y = x, x = pf[x]) {
splay(x, 0); fa[t[x][1]] = 0; pf[t[x][1]] = x;
t[x][1] = y; fa[y] = x; pf[y] = 0;
}
}
void makeroot(int x) {
access(x); splay(x, 0); fan(x);
}
void link(int x, int y) {
makeroot(x); pf[x] = y; access(x);
}
void cut(int x, int y) {
makeroot(x); access(y); splay(y, 0);
t[y][0] = fa[x] = pf[x] = 0;
update(y);
}
int n, A, B, q;
struct edge {
int u, v, k;
}a[N * 2], b[N * 2];
bool rank(edge a, edge b) {
return a.k < b.k;
}
int f[N];
int find(int x) {
return f[x] == x ? x : (f[x] = find(f[x]));
}
void BT(edge *a, int A) {
sort(a + 1, a + A + 1, rank);
fo(i, 1, n) f[i] = i;
int tot = 0;
fo(i, 1, A) {
int x = a[i].u, y = a[i].v, k = a[i].k;
if(find(x) != find(y)) {
f[f[x]] = f[y];
a[++ tot] = a[i];
}
if(tot == n - 1) break;
}
}
struct node {
int x; ll y;
} d[N];
bool rank_d(node a, node b) {return a.x < b.x;}
int main() {
freopen("graph.in", "r", stdin);
freopen("graph.out", "w", stdout);
scanf("%d %d %d %d", &n, &A, &B, &q);
fo(i, 1, A) scanf("%d %d %d", &a[i].u, &a[i].v, &a[i].k);
fo(i, 1, B) scanf("%d %d %d", &b[i].u, &b[i].v, &b[i].k);
BT(a, A); BT(b, B);
fo(i, 0, n) z[i] = -2e9;
ll sumk = 0;
int te = n;
fo(i, 1, n - 1) {
int x = a[i].u, y = a[i].v, k = a[i].k;
sumk += k;
te ++;
z[te] = k;
link(x, te); link(y, te);
}
int d0 = 0;
fo(i, 1, n - 1) {
int x = b[i].u, y = b[i].v, k = b[i].k;
makeroot(x); access(y); splay(y, 0);
int mb = mx[y];
if(mb > n + n - 1 || mb <= n) continue;
int x1 = a[mb - n].u, y1 = a[mb - n].v, k1 = a[mb - n].k;
cut(x1, mb); cut(y1, mb);
te ++; z[te] = -2e9;
link(x, te); link(y, te);
d[++ d0].x = (k - k1 + 1) / 2; d[d0].y = k - k1;
}
sort(d + 1, d + d0 + 1, rank_d);
fo(i, 2, d0) d[i].y += d[i - 1].y;
fo(ii, 1, q) {
int u; scanf("%d", &u);
int ans = 0;
for(int l = 1, r = d0; l <= r; ) {
int m = l + r >> 1;
if(d[m].x <= u)
ans = m, l = m + 1; else r = m - 1;
}
printf("%lld\n", sumk + d[ans].y + (ll)(n - 1 - ans * 2) * u);
}
}