【NOIP2017提高A组集训10.28】图

Description:

有一个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);
    }
}

你可能感兴趣的:(lct,贪心)