题意:
一个有n个城市m条道路的图,如果一条边删除后,有些城市变得不可达,则它需要维护,花费为L×D,L是道路长度,D是变得不可达的城市对数。每条道路维护花费由连接的城市中的一个承担。城市本来也有固定的维护花费。求所有可能中,花费最大的城市的最小花费。
题解:
首先可以用tarjan求桥,如果(u,v)之间为桥,且u为父亲,则回溯时已遍历的点数-dfn[v]+1就得到和v在同一双连通分量的城市数。
然后由于桥的性质,将所有非桥边删去后图就变成了森林。对于每一棵树可以单独求最小的最大可能花费:
二分答案,然后遍历树,对于当前点而言,如果任意儿子的子树不能将边全部维护,则这答案不可行;
其次,每条连向儿子的边尽可能让儿子维护,否则父亲维护,如果当前点不能维护所有的这样的边,答案不可行;
看看当前点是否能再维护到父亲的边。
//Time:552ms
//Length:2742B
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define MAXN 40010
#define MOD 1000000007
int he[MAXN],to[MAXN],nex[MAXN],len[MAXN],pri[MAXN],top;
int dfn[MAXN],low[MAXN],num,sta[MAXN],stop,cnt[MAXN];
bool is[MAXN],vi[MAXN],cou[MAXN][2];
void add(int u,int v,int w)
{
to[top]=v;
len[top]=w;
nex[top]=he[u];
he[u]=top++;
}
void tarjan(int u,int pre)
{
dfn[u] = low[u] = num++;
sta[stop++]=u;
for(int i = he[u]; i != -1; i = nex[i])
{
int v = to[i];
if(v==pre) continue;
if(dfn[v]==-1)
{
tarjan(v,u);
if(low[v]>dfn[u])
{
is[i^1] = is[i]=1;
cnt[i^1]=cnt[i]=num-dfn[v];
}
low[u]=min(low[u],low[v]);
}
else low[u] = min(low[u],dfn[v]);
}
}
bool dfs(int h,long long l,long long lim)
{
long long sum=pri[h];
cou[h][0]=cou[h][1]=0;
vi[h]=1;
for(int i=he[h];i!=-1;i=nex[i])
if(is[i]&&!vi[to[i]])
{
long long tmp=(long long)len[i]*cnt[i]*(stop-cnt[i]);
if(!dfs(to[i],tmp,lim)) return false;
if(!cou[to[i]][0]) return false;
if(!cou[to[i]][1]) sum+=tmp;
}
if(sum<=lim) cou[h][0]=1;
else return false;
if(sum+l<=lim) cou[h][1]=1;
return true;
}
bool check(long long lim)
{
for(int i=0;i<stop;++i) vi[sta[i]]=0;
for(int i=0;i<stop;++i)
if(!vi[sta[i]])
if(!dfs(sta[i],0,lim))
return false;
return true;
}
int main()
{
//freopen("/home/moor/Code/input","r",stdin);
int cas,n,m;
long long ans,l,r,mid;
scanf("%d",&cas);
for(int hh=1;hh<=cas;++hh)
{
scanf("%d%d",&n,&m);
l=0;
for(int i=1;i<=n;++i) scanf("%d",&pri[i]),l=max(l,(long long)pri[i]);
memset(he,-1,sizeof(he));
memset(dfn,-1,sizeof(dfn));
memset(is,0,sizeof(is));
memset(vi,0,sizeof(vi));
memset(cou,0,sizeof(cou));
top=0;
for(int i=0;i<m;++i)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
add(b,a,c);
}
ans=l;
for(int i=1;i<=n;++i)
if(dfn[i]==-1)
{
num=0;
stop=0;
tarjan(i,-1);
l=ans;
r=1e15;
while(l<r)
{
mid=(l+r)/2;
if(check(mid)) r=mid;
else l=mid+1;
}
ans=max(ans,l);
}
printf("Case %d: ",hh);
cout<<ans<<'\n';
}
return 0;
}