题目链接如下:http://codeforces.com/problemset/problem/733/F
算法思路
这道题让我又加深了一下对于倍增二分求LCA的原理.
很显然,要让每两个城市之间两两可达,需要构造一棵最小生成树。但是我们在这里可以使用一定的钱减少一些边的dissatisfation,而且这个值可以为负值,很显然我们要选择的就是成本最小的那一条边.这样的话,我们先使用最小生成树算法构造出最小生成树,然后对于每条边,加入最小生成树之后会构成环,所以对于每个环,我们要计算出这个环上权值最大的边。
这里使用的就是倍增,二分的方法,dp[i][j]表示的是第i个结点以上
2j
层的祖先,那么对于两个不同深度的点,先将它们提升到同一个高度,然后在二分最长的边,具体的做法如下:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<utility>
#include<vector>
using namespace std;
#define MAXN 200005
#define LL long long
#define INF 0x3f3f3f3f3f3f3f3f
struct Edge{
int u,v;
int index;
LL c,w;
}EdgeTable[MAXN];
int n,m;
LL k,tot;
vector<pair<int,int> >grid[MAXN];
int fa[MAXN];
int height[MAXN],psb[MAXN][22];
LL dsb[MAXN][22];
bool comp(Edge a,Edge b)
{
return a.w<b.w;
}
int find_dad(int cur)
{
if(fa[cur]==cur)return cur;
else return fa[cur]=find_dad(fa[cur]);
}
void Kruskal()
{
int i;
int num=0;
sort(EdgeTable+1,EdgeTable+1+m,comp);
tot=0;
for(i=1;i<=n;i++)
fa[i] = i;
for(i=1;i<=m;i++){
int u = EdgeTable[i].u;
int v = EdgeTable[i].v;
int fau = find_dad(u);
int fav = find_dad(v);
if(fau == fav)continue;
else{
tot += EdgeTable[i].w;
fa[fau] = fav;
grid[u].push_back(make_pair(v,i));
grid[v].push_back(make_pair(u,i));
num++;
if(num==n-1)break;
}
}
return;
}
void Dfs(int cur,int fa)
{
int i,j,k;
for(i=0;i<grid[cur].size();i++){
j = grid[cur][i].first;
k = grid[cur][i].second;
if(j!=fa){
height[j] = height[cur]+1;
psb[j][0] = cur;
dsb[j][0] = EdgeTable[k].w;
Dfs(j,cur);
}
}
return;
}
LL query(int a,int b)
{
int i=0;
LL ans = 0;
if(height[a]>height[b])return query(b,a);
for(i=19;i>=0;i--){
if(height[b]-(1<<i)>=height[a]){
ans = max(ans,dsb[b][i]);
b = psb[b][i];
}
}
if(a==b)return ans;
for(i=19;i>=0;i--){
if(psb[a][i]!=psb[b][i]){
ans = max(dsb[a][i],ans);
ans = max(dsb[b][i],ans);
a = psb[a][i];
b = psb[b][i];
}
}
return max(ans,max(dsb[a][0],dsb[b][0]));
}
void Solve()
{
memset(psb,-1,sizeof(psb));
Kruskal();
height[1]=1;
Dfs(1,-1);
int i,j;
LL ans = INF;
int select = 1;
for(i=1;i<20;i++){
for(j=1;j<=n;j++){
if(psb[j][i-1]>=0){
psb[j][i] = psb[psb[j][i-1]][i-1];
dsb[j][i] = max(dsb[j][i-1],dsb[psb[j][i-1]][i-1]);
}
}
}
for(i=1;i<=m;i++){
//cout << ans << endl;
LL tmp = tot - query(EdgeTable[i].u,EdgeTable[i].v) - k/EdgeTable[i].c + EdgeTable[i].w;
if(tmp<ans){
ans = tmp;
select = i;
}
}
printf("%lld\n",ans);
for(i=1;i<=n;i++)
fa[i]=i;
fa[EdgeTable[select].u] = EdgeTable[select].v;
printf("%d %lld\n",EdgeTable[select].index,EdgeTable[select].w-k/EdgeTable[select].c);
int num=1;
for(i=1;i<=m;i++){
int u = EdgeTable[i].u;
int v = EdgeTable[i].v;
int fau = find_dad(u);
int fav = find_dad(v);
if(fau == fav)continue;
else{
printf("%d %lld\n",EdgeTable[i].index,EdgeTable[i].w);
fa[fau] = fav;
num++;
if(num==n-1)break;
}
}
return;
}
int main()
{
//freopen("input","r",stdin);
int i;
scanf("%d%d",&n,&m);
for(i=1;i<=m;i++){
scanf("%lld",&EdgeTable[i].w);
EdgeTable[i].index=i;
}
for(i=1;i<=m;i++)
scanf("%lld",&EdgeTable[i].c);
for(i=1;i<=m;i++)
scanf("%d%d",&EdgeTable[i].u,&EdgeTable[i].v);
scanf("%lld",&k);
Solve();
return 0;
}