首先要证明只修一条路是最优的。
只证两条路的情况,多条路同理。
假设同时修了两条路x,y。
如果这两条路最后都没有在最小生成树中,那么修理都白费了,不如把资金都投入到其中一条路上,这样这条路才有可能成为新生成树的一部分,从而减小总长度。
如果有且只有一条在最小生成树中,不妨设x在,y不在。那么花在y上的钱都白费了,不如全花在x上,让总长度进一步减小。或者把花在x上的钱花在y上,试图让y不白花。
如果两者都在最小生成树中,那么不如选择修理费更小的那一条,就能节约更多资金,从而使总长度更小。
证毕。
方法就是先对初始道路跑一遍最小生成树,求出总长度ans。然后枚举需要修理的那条道路,并将全部资金花在修理它上。得到一条新的道路。
如果这条道路已经在最小生成树中,那么把ans一减就是修理后的答案。
否则就需要对道路进行替换。
假设这条道路连接u,v。那么最先被替换的那条道路一定是u,v间长度最大的那条道路。然后ans一加一减就是修理后的答案。
想要快速求出树上u,v间的最长道路,需要用到树链剖分。然后线段树维护区间最大值以及最大值的编号即可。
代码
#include<bits/stdc++.h>
#define ls (now<<1)
#define rs (ls|1)
#define maxn 200010
using namespace std;
typedef long long ll;
typedef pair<ll,ll> pii;
const ll INF=LONG_LONG_MAX>>2;
ll n,m,S;
/
//Edge
struct Edge
{
ll to,val,next,id;
}edges[maxn<<1];
ll head[maxn],tot;
void init()
{
tot=0;
memset(head,-1,sizeof(head));
}
void add(ll from,ll to,ll val,ll id)
{
edges[tot].to=to;
edges[tot].val=val;
edges[tot].id=id;
edges[tot].next=head[from];
head[from]=tot++;
}
/
/
//Kruskal
ll u[maxn],v[maxn],w[maxn],c[maxn];
bool cmp(ll i,ll j){return w[i]<w[j];}
ll r[maxn],fa[maxn];
ll f(ll x){return fa[x]==x?x:fa[x]=f(fa[x]);}
bool vis[maxn];
ll Kruskal()
{
ll ret=0;
for(ll i=1;i<=n;i++) fa[i]=i;
for(ll i=1;i<=m;i++) r[i]=i;
sort(r+1,r+m+1,cmp);
for(ll i=1;i<=m;i++)
{
ll e=r[i];
ll x=f(u[e]),y=f(v[e]);
if(x!=y)
{
add(u[e],v[e],w[e],e);
add(v[e],u[e],w[e],e);
fa[x]=y;
vis[e]=true;
ret+=w[e];
}
}
return ret;
}
/
///
//线段树
pii tree[maxn<<2];
inline void up_data(ll now)
{
tree[now]=max(tree[ls],tree[rs]);
}
void A(ll l,ll r,ll now,ll pos,ll val)
{
if(l==r)
{
tree[now]=make_pair(val,pos);
return;
}
ll m=(l+r)>>1;
if(pos<=m) A(l,m,ls,pos,val);
else A(m+1,r,rs,pos,val);
up_data(now);
}
pii Q(ll l,ll r,ll now,ll ql,ll qr)
{
if(ql<=l&&r<=qr) return tree[now];
ll m=(l+r)>>1;
pii ansl=make_pair(-INF,-INF),ansr=make_pair(-INF,-INF);
if(qr>m) ansr=Q(m+1,r,rs,ql,qr);
if(ql<=m) ansl=Q(l,m,ls,ql,qr);
return max(ansl,ansr);
}
/
//树链剖分
ll siz[maxn],father[maxn],son[maxn],dep[maxn];
void dfs1(ll u,ll f,ll d)
{
dep[u]=d;
father[u]=f;
siz[u]=1;
son[u]=-1;
for(ll i=head[u];~i;i=edges[i].next)
{
ll v=edges[i].to;
if(v==f) continue;
dfs1(v,u,d+1);
siz[u]+=siz[v];
if(son[u]==-1||siz[v]>siz[son[u]])
son[u]=v;
}
}
ll tim,tid[maxn],top[maxn],rk[maxn];
void dfs2(ll u,ll tp)
{
top[u]=tp;
tid[u]=++tim;
rk[tid[u]]=u;
if(son[u]==-1) return;
dfs2(son[u],tp);
for(ll i=head[u];~i;i=edges[i].next)
{
ll v=edges[i].to;
if(v==father[u]) continue;
if(v==son[u])
{
A(1,n,1,tid[v]-1,edges[i].val);
continue;
}
dfs2(v,v);
A(1,n,1,tid[v]-1,edges[i].val);
}
}
///
pii QQ(ll x,ll y)
{
pii ret=make_pair(-INF,-INF);
ll tpx=top[x],tpy=top[y];
while(x!=y)
{
if(tpx!=tpy)
{
if(dep[tpx]>dep[tpy])
{
swap(x,y);
swap(tpx,tpy);
}
ret=max(ret,Q(1,n,1,tid[tpy]-1,tid[y]-1));
y=father[tpy];
tpy=top[y];
}
else
{
if(dep[x]>dep[y])
{
swap(x,y);
swap(tpx,tpy);
}
ret=max(ret,Q(1,n,1,tid[x],tid[y]-1));
return ret;
}
}
return ret;
}
void print(ll kjia,ll kjian)
{
if(kjia==-1)
{
for(ll i=1;i<=m;i++)
if(vis[i])
printf("%I64d %I64d\n",i,w[i]);
}
else if(kjian==-1)
{
for(ll i=1;i<=m;i++)
if(vis[i])
{
if(i==kjia)
printf("%I64d %I64d\n",i,w[i]-S/c[i]);
else
printf("%I64d %I64d\n",i,w[i]);
}
}
else
{
for(ll i=1;i<=m;i++)
{
if(i==kjia)
printf("%I64d %I64d\n",i,w[i]-S/c[i]);
else if(vis[i])
{
if(i==kjian) continue;
printf("%I64d %I64d\n",i,w[i]);
}
}
}
}
void solve(ll chu)
{
ll ANS=chu;
ll kjia=-1,kjian=-1;
for(ll i=1;i<=m;i++)
if(vis[i])
{
ll jian=S/c[i];
if(ANS>chu-jian)
{
ANS=chu-jian;
kjia=i;
kjian=-1;
}
}
else
{
pii MAX=QQ(u[i],v[i]);
ll MIN=w[i]-S/c[i];
ll XIN=chu-MAX.first+MIN;
if(ANS>XIN)
{
ANS=XIN;
kjia=i;
kjian=rk[MAX.second+1];
for(ll i=head[kjian];~i;i=edges[i].next)
if(edges[i].to==father[kjian])
{
kjian=edges[i].id;
break;
}
}
}
printf("%I64d\n",ANS);
print(kjia,kjian);
}
int main()
{
init();
scanf("%I64d %I64d",&n,&m);
for(ll i=1;i<=m;i++)
scanf("%I64d",&w[i]);
for(ll i=1;i<=m;i++)
scanf("%I64d",&c[i]);
for(ll i=1;i<=m;i++)
scanf("%I64d %I64d",&u[i],&v[i]);
scanf("%I64d",&S);
ll chu=Kruskal();
dfs1(1,-1,1);
dfs2(1,1);
solve(chu);
return 0;
}