题意:给出一个图,每条边有权值和花费c,每次花费c能使的权值-1。给出一个预算,求减完权值后的一个最小生成树。
观察到最优的策略必然是只减少一条边的权值。于是首先先将初始权值做一次最小生成树。然后枚举所有的边,如果这条边是生成树的边,求出预算都花在这条边上的结果;如果非树边,就在这条边两个点到他们的LCA路径上求出树边的最大值,这个最大值用轻重链剖分维护,(也可以往上倍增)树链上无修改的最大值可以用RMQ维护,然后求出扣掉这个最大树边,加进去这条边的最终结果。中间过程维护一下最小值。
#include
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
#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<for (int i = 0; i+(1<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<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;
}