CodeForces 733 F.Drivers Dissatisfaction(最小生成樹-Kruskal+線上倍增法)
阿新 • • 發佈:2018-12-24
Description
給出n個點m條邊,每條邊有邊權,第i條邊邊權為w[i],可以花費c[i]使得第i條邊邊權減一,在花費不超過S的情況下求最小生成樹
Input
第一行兩個整數n和m分別表示點數和邊數,之後m個整數w[i]表示第i條邊的邊權,之後m個整數c[i]表示減少第i條邊單位邊權需要的花費,之後m行每行兩個整數u和v表示u和v之間有一條邊,最後一行一整數S表示總花費(2<=n<=2e5,n-1<=m<=2e5,1<=w[i],c[i]<=1e9,0<=S<=1e9)
Output
輸出最小生成樹權值和,然後輸出每條樹邊的編號和邊權
Sample Input
6 9
1 3 1 1 3 1 2 2 2
4 1 4 2 2 5 3 1 6
1 2
1 3
2 3
2 4
2 5
3 5
3 6
4 5
5 6
7
Sample Output
0
1 1
3 1
6 1
7 2
8 -5
Solution
必然是要把所有錢都用來減一條邊更優,首先求一遍MST,那麼花費要用來減樹邊中c[i]最小的那條邊,之後如果要減去一條非樹邊u-v的邊權使得其成為之後新圖的MST中一條邊的話,那就要刪去原MST中u-v之間路徑中邊權最大值才能保證之後的新MST邊權和儘量小,所以用線上倍增法維護樹上邊權最小值,之後列舉每條非樹邊考慮全部減掉其邊權來更新最優解即可
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
#define maxn 222222
struct node
{
int u,v,w,id;
bool operator<(const node&b)const
{
return w<b.w;
}
}edge[maxn];
int T,n,m,fa[maxn],W[maxn],C[maxn];
int pos,vis[maxn],deep[maxn],p[maxn][21],Max[maxn][21];
ll S,ans;
typedef pair<int,int>P;
vector<P>G[maxn];
void add(int u,int v,int id)
{
G[u].push_back(P(v,id)),G[v].push_back(P(u,id));
}
int find(int x)
{
if(fa[x]==x) return x;
return fa[x]=find(fa[x]);
}
ll kruskal(int n,int m)
{
for(int i=1;i<=n;i++)fa[i]=i;
for(int i=1;i<=n;i++)G[i].clear();
int cnt=0,temp=2000000000;
ll ans=0;
sort(edge+1,edge+m+1);
for(int k=1;k<=m;k++)
{
int u=edge[k].u,v=edge[k].v,w=edge[k].w,id=edge[k].id;
int x=find(u),y=find(v);
if(x!=y)
{
cnt++;
fa[x]=y;
ans+=w;
if(temp>C[id])temp=C[id],pos=id;
add(u,v,id);
vis[id]=1;
if(cnt==n-1)return ans;
}
}
return -1;
}
void dfs(int u,int f,int w)
{
deep[u]=deep[f]+1,p[u][0]=f,Max[u][0]=w;
for(int i=1;i<=20;i++)
{
if(deep[u]-(1<<i)<=0)break;
int v=p[u][i-1];
p[u][i]=p[v][i-1];
if(W[Max[u][i-1]]<W[Max[v][i-1]])Max[u][i]=Max[v][i-1];
else Max[u][i]=Max[u][i-1];
}
for(int i=0;i<G[u].size();i++)
{
int v=G[u][i].first,w=G[u][i].second;
if(v!=f)dfs(v,u,w);
}
}
int get_max(int x,int y)
{
if(deep[x]<deep[y])swap(x,y);
int temp=0,ans=0;
for(int i=20;i>=0;i--)
if(deep[x]-(1<<i)>=deep[y])
{
if(W[Max[x][i]]>temp)ans=Max[x][i],temp=W[ans];
x=p[x][i];
}
if(x==y)return ans;
for(int i=20;i>=0;i--)
if(deep[x]-(1<<i)>0&&p[x][i]!=p[y][i])
{
if(W[Max[x][i]]>temp)ans=Max[x][i],temp=W[ans];
if(W[Max[y][i]]>temp)ans=Max[y][i],temp=W[ans];
x=p[x][i],y=p[y][i];
if(x==y)break;
}
if(W[Max[x][0]]>temp)ans=Max[x][0],temp=W[ans];
if(W[Max[y][0]]>temp)ans=Max[y][0],temp=W[ans];
return ans;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)scanf("%d",&W[i]);
for(int i=1;i<=m;i++)scanf("%d",&C[i]);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&edge[i].u,&edge[i].v);
edge[i].w=W[i],edge[i].id=i;
}
memset(vis,0,sizeof(vis));
ll sum=kruskal(n,m);
scanf("%I64d",&S);
ans=sum-S/C[pos];
deep[0]=0;
dfs(1,0,0);
int del=0;
for(int i=1;i<=m;i++)
{
int u=edge[i].u,v=edge[i].v,id=edge[i].id;
if(vis[id])continue;
int t=get_max(u,v);
if(t==0)continue;
ll temp=sum-W[t]+W[id]-S/C[id];
if(temp<ans)ans=temp,pos=id,del=t;
}
if(del)vis[del]=0,vis[pos]=1;
printf("%I64d\n",ans);
W[pos]-=S/C[pos];
for(int i=1;i<=m;i++)
if(vis[i])
printf("%d %d\n",i,W[i]);
return 0;
}