题目
Luogu
题目大意:
n
n
n 个点
m
m
m 条边无向连通图,有些边是你的可以收费,建立最小生成树方式你定,问最大获利
k
≤
20
k\le 20
k≤20
思路
由于收费边集很小
首先把所有收费边加入集合建立最小生成树,去掉收费边剩下构成了
k
+
1
k+1
k+1 个连通块,进行缩点,然后枚举收费边集跑
K
r
u
s
k
a
l
Kruskal
Kruskal ,那么对于一条边的最大收费可以利用破圈思想进行取
m
i
n
min
min 更新,这里由于点很少,暴力爬链
时间复杂度
O
(
m
l
o
g
m
+
2
k
k
2
)
O(mlogm+2^kk^2)
O(mlogm+2kk2)
代码
#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<cstdio>
#include<queue>
#include<vector>
#include<cstring>
#include<climits>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
int read(){
int f=1,x=0;char c=getchar();
while(c<'0'||'9'<c){if(c=='-')f=-1;c=getchar();}
while('0'<=c&&c<='9'){x=x*10+c-'0';c=getchar();}
return f*x;
}
#define fi first
#define se second
#define mp make_pair
const int MAXK=20;
const int MAXN=300000;
const int INF=0x3f3f3f3f;
typedef pair<int,int> pii;
struct Edge{
int u,v,w;
friend bool operator < (Edge a,Edge b){return a.w<b.w;}
}edge1[MAXN+5],edge3[MAXK*MAXK+5];
pii edge2[MAXN+5];
bool cho[MAXN+5],mark[MAXK+5];
LL val[MAXN+5],ans,sum[MAXN+5];
int n,m,k,pre[MAXN+5],tp,icnt,id[MAXK+5];
int rt,mn[MAXN+5],dep[MAXN+5],fa[MAXN+5];
struct E{
int v,nxt;
}edge[MAXK*MAXK+5];
int ecnt,head[MAXN+5];
void Addedge(int u,int v){
edge[++ecnt]=(E){v,head[u]},head[u]=ecnt;
edge[++ecnt]=(E){u,head[v]},head[v]=ecnt;
return ;
}
int Find(int u){return pre[u]==u?u:(pre[u]=Find(pre[u]));}
void DFS(int u){
sum[u]=val[u];
for(int i=head[u];i;i=edge[i].nxt){
int v=edge[i].v;
if(v==fa[u]) continue;
fa[v]=u,dep[v]=dep[u]+1,DFS(v),sum[u]+=sum[v];
}
return ;
}
void Kruskal(){
ecnt=0;
for(int i=1;i<=k+1;i++)
head[id[i]]=0,fa[id[i]]=0,dep[id[i]]=0,mn[id[i]]=INF,pre[id[i]]=id[i];
for(int i=1;i<=k;i++)
if(mark[i]){
int u=Find(edge2[i].fi),v=Find(edge2[i].se);
if(u==v) return ;
pre[u]=v;
Addedge(edge2[i].fi,edge2[i].se);
}
for(int i=1;i<=k;i++){
int u=Find(edge3[i].u),v=Find(edge3[i].v);
if(u==v) continue;
pre[u]=v;
Addedge(edge3[i].u,edge3[i].v);
}
DFS(rt);
for(int i=1;i<=k;i++){
int u=edge3[i].u,v=edge3[i].v,w=edge3[i].w;
if(dep[u]<dep[v]) swap(u,v);
while(dep[u]!=dep[v]) mn[u]=min(mn[u],w),u=fa[u];
while(u!=v){
mn[u]=min(mn[u],w),u=fa[u];
mn[v]=min(mn[v],w),v=fa[v];
}
}
LL ret=0;
for(int i=1;i<=k;i++)
if(mark[i]){
int u=edge2[i].fi,v=edge2[i].se;
if(dep[u]<dep[v]) swap(u,v);
ret+=mn[u]*sum[u];
}
ans=max(ans,ret);
return ;
}
int main(){
n=read(),m=read(),k=read();
for(int i=1;i<=m;i++)
edge1[i].u=read(),edge1[i].v=read(),edge1[i].w=read();
for(int i=1;i<=k;i++)
edge2[i].fi=read(),edge2[i].se=read();
for(int i=1;i<=n;i++)
val[i]=read();
sort(edge1+1,edge1+m+1);
for(int i=1;i<=n;i++)
pre[i]=i;
for(int i=1;i<=k;i++)
pre[Find(edge2[i].fi)]=Find(edge2[i].se);
for(int i=1;i<=m;i++){
int u=Find(edge1[i].u),v=Find(edge1[i].v);
if(u==v) continue;
cho[i]=1,pre[u]=v;
}
for(int i=1;i<=n;i++)
pre[i]=i;
for(int i=1;i<=m;i++)
if(cho[i])
pre[Find(edge1[i].u)]=Find(edge1[i].v);
rt=Find(1);
for(int i=1,t;i<=n;i++)
if((t=Find(i))!=i)
val[t]+=val[i];
else id[++icnt]=i;
for(int i=1;i<=m;i++)
edge1[i].u=Find(edge1[i].u),edge1[i].v=Find(edge1[i].v);
for(int i=1;i<=k;i++)
edge2[i].fi=Find(edge2[i].fi),edge2[i].se=Find(edge2[i].se);
for(int i=1;i<=m;i++){
int u=Find(edge1[i].u),v=Find(edge1[i].v);
if(u==v) continue;
edge3[++tp]=edge1[i];
pre[u]=v;
}
for(int s=1;s<(1<<k);s++){
for(int i=1;i<=k;i++)
if((s>>(i-1))&1)
mark[i]=1;
else mark[i]=0;
Kruskal();
}
printf("%lld\n",ans);
return 0;
}