内网传送门
外网传送门
枚举每条边的占领情况然后判断即可,复杂度 O ( 2 m ) (2^m) (2m)
最后被占领的桥以及桥联通的点一定形成若干联通块。
注意到一个联通块内部的答案为 m a x ( m a x    a i , m a x    c i ) ∗ m i n    b i max(max \;a_i,max\;c_i)∗min \;b_i max(maxai,maxci)∗minbi
考虑按 c c c 从小到大加入每条边,加入一条边的时候若两端已经联通,那么这条边肯定没用了,于是最后的图形成了若干棵生成树。可以分开计算答案。
直接暴力树形 D P DP DP,记 f i , j , k f_{i,j,k} fi,j,k 为 i i i 的子树中,与 i i i 联通的权值最大为 j j j,最小为 k k k 的最小花费。
因为可能的值只有 O ( n ) (n) (n) 个,所以状态数是 O ( n 3 ) (n^3) (n3) 的。
合并两个子树的答案需要 O ( n 4 ) (n^4) (n4) 的时间,总的时间为 O ( n 5 ) (n^5) (n5)。
可以证明,最优方案一定是生成最小生成树过程中的某个阶段。
于是我们做最小生成树的时候维护一下联通块的最大最小值和最优答案,合并的时候更新一下答案就好了。
时间复杂度 O ( ( m + n ) ∗ l o g    n ) ((m+n)*log\;n) ((m+n)∗logn)。
#include
#include
#include
#define N 100005
#define M 200005
using namespace std;
int a[N],b[N],father[N];
long long val[N],Max[N],Min[N];
struct edge
{
int u,v,w;
}e[M];
int find(int x)
{
if(father[x]!=x)
father[x]=find(father[x]);
return father[x];
}
bool comp(const edge &p,const edge &q)
{
return p.w<q.w;
}
int main()
{
// freopen("bit.in","r",stdin);
// freopen("bit.out","w",stdout);
int n,m,i,x,y;
long long sum=0;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i)
{
father[i]=i;
scanf("%d%d",&a[i],&b[i]);
Max[i]=a[i],Min[i]=b[i];
val[i]=a[i]*b[i],sum+=val[i];
}
long long ans=sum;
for(i=1;i<=m;++i)
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].w);
sort(e+1,e+m+1,comp);
for(i=1;i<=m;++i)
{
x=find(e[i].u);
y=find(e[i].v);
if(x==y) continue;
father[x]=y;
val[y]+=val[x];
Min[y]=min(Min[x],Min[y]);
Max[y]=max(max(Max[x],Max[y]),1ll*e[i].w);
sum-=val[y];val[y]=min(val[y],1ll*Min[y]*Max[y]);sum+=val[y];
ans=min(ans,sum);
}
printf("%lld",ans);
// fclose(stdin);
// fclose(stdout);
return 0;
}