codeforces 733F (樹鏈剖分 RMQ)

題目鏈接:點擊這裏

題意:給出一個圖,每條邊有權值和花費c,每次花費c能使的權值-1。給出一個預算,求減完權值後的一個最小生成樹。

觀察到最優的策略必然是隻減少一條邊的權值。於是首先先將初始權值做一次最小生成樹。然後枚舉所有的邊,如果這條邊是生成樹的邊,求出預算都花在這條邊上的結果;如果非樹邊,就在這條邊兩個點到他們的LCA路徑上求出樹邊的最大值,這個最大值用輕重鏈剖分維護,(也可以往上倍增)樹鏈上無修改的最大值可以用RMQ維護,然後求出扣掉這個最大樹邊,加進去這條邊的最終結果。中間過程維護一下最小值。

#include <bits/stdc++.h>
using namespace std;
#define Clear(x,y) memset (x,y,sizeof(x));
#define maxn 200005
#define maxm maxn<<1

int n, m;
long long w[maxn], c[maxn], S;
struct E {
    int u, v, id;
    bool operator < (const E &a) const {
        return w[id] < w[a.id];
    }
}e[maxn];
bool in_mst[maxn];//每條邊在不在mst中

struct node {
    int v, next, id;
}edge[maxm];
int head[maxn], cnt;
int top[maxn], fa[maxn], deep[maxn], num[maxn], p[maxn], fp[maxn];
int son[maxn], pos;
long long MST;

void add_edge (int u, int v, int id) {
    edge[cnt].id = id;
    edge[cnt].v = v, edge[cnt].next = head[u], head[u] = cnt++;
}

int Find (int x) {return fa[x] == x ? fa[x] : fa[x] = Find (fa[x]);}
void init () {
    MST = 0;
    Clear (in_mst, 0);
    Clear (head, -1);
    cnt = 0;
    for (int i = 1; i <= n; i++) fa[i] = i;
    for (int i = 1; i <= m; i++) {// cout << e[i].id << " ";
        int p1 = Find (e[i].u), p2 = Find (e[i].v);
        if (p1 != p2) {
            fa[p1] = p2;
            MST += w[e[i].id];
            in_mst[e[i].id] = 1;
            add_edge (e[i].u, e[i].v, e[i].id);
            add_edge (e[i].v, e[i].u, e[i].id);
        }
    } //cout << endl;
    Clear (son, -1);
    pos = 0;
    //for (int i = 1; i <= m; i++) if (in_mst[i]) cout << i << " "; cout << endl;
    //cout << MST << endl;
}

void dfs1 (int u, int pre, int d) {
    deep[u] = d;
    fa[u] = pre;
    num[u] = 1;
    for (int i = head[u]; i != -1; i = edge[i].next) {
        int v = edge[i].v;
        if (v == pre) continue;
        dfs1 (v, u, d+1);
        num[u] += num[v];
        if (son[u] == -1 || num[v] > num[son[u]]) son[u] = v;
    }
}

#define pii pair<long long, int>
#define mp make_pair
pii dp[maxn][20];
pii min (pii a, pii b) {
    return a < b ? a : b;
}
pii max (pii a, pii b) {
    return a < b ? b : a;
}

void getpos (int u, int sp) {
    top[u] = sp;
    p[u] = pos++;
    fp[p[u]] = u;
    if (son[u] == -1) return ;
    getpos (son[u], sp);
    for (int i = head[u]; i != -1; i = edge[i].next) {
        int v = edge[i].v;
        if (v == fa[u] || v == son[u]) continue;
        getpos (v, v);
    }
}

void dfs2 (int u) {
    for (int i = head[u]; i != -1; i = edge[i].next) {
        int v = edge[i].v; if (v == fa[u]) continue;
        dp[p[v]][0] = mp (w[edge[i].id], edge[i].id);
        dfs2 (v);
    }
}

void rmq_init () {
    dfs2 (1);
    for (int j = 1; (1<<j) <= n; j++) {
        for (int i = 0; i+(1<<j)-1 < n; i++) {
            dp[i][j] = max (dp[i][j-1], dp[i+(1<<(j-1))][j-1]);
        }
    }
}

pii rmq (int l, int r) {
    int k = 0;
    while ((1<<(k+1)) <= r-l+1) k++;
    return max (dp[l][k], dp[r-(1<<k)+1][k]);
}

pii query (int u, int v) {
    int f1 = top[u], f2 = top[v];
    pii tmp = mp (0, 0);
    while (f1 != f2) {
        if (deep[f1] < deep[f2]) {
            swap (u, v);
            swap (f1, f2);
        }
        tmp = max (tmp, rmq (p[f1], p[u]));
        u = fa[f1]; f1 = top[u];
    }
    if (u == v) return tmp;
    if (deep[u] > deep[v]) swap (u, v);
    return max (tmp, rmq (p[son[u]], p[v]));
}

void solve () {
    dfs1 (1, 0, 0);
    getpos (1, 1);
    rmq_init ();
    long long ans = 1e15; int pos = -1, del = -1;
    for (int i = 1; i <= m; i++) {
        long long tmp;
        if (in_mst[e[i].id]) {
            tmp = MST-S/c[e[i].id];
            if (tmp < ans) {
                ans = tmp;
                pos = e[i].id;
            }
        }
        else {
            pii Min = query (e[i].u, e[i].v);
            tmp = MST-w[Min.second]+w[e[i].id]-S/c[e[i].id];
            if (tmp < ans) {
                ans = tmp;
                pos = e[i].id;
                del = Min.second;
            }
        }
    }
    cout << ans << "\n";
    for (int i = 1; i <= m; i++) if (in_mst[i] || i == pos) {
        if (i == del) continue;
        cout << i << " ";
        if (i == pos) cout << w[i]-S/c[i] << "\n";
        else cout << w[i] << "\n";
    }
}

int main () {
    //freopen ("more.in", "r", stdin);
    ios::sync_with_stdio (0);
    cin >> n >> m;
    for (int i = 1; i <= m; i++) cin >> w[i];
    for (int i = 1; i <= m; i++) cin >> c[i];
    for (int i = 1; i <= m; i++) {
        cin >> e[i].u >> e[i].v;
        e[i].id = i;
    }
    cin >> S;
    sort (e+1, e+1+m);
    init ();
    solve ();
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章