[题解] P3639 [APIO2013]道路费用
发现
考虑枚举加入新边的集合,如果直接跑最小生成树的话会 T,需要优化
容易发现每次最小生成树重复加入的边数会很多,于是可以考虑哪些边是一定会加入
我们先把
接下来把这些边组成的连通块缩成一个点,有
现在在枚举边集的时候跑的最小生成树只要在缩点后的图上把枚举的边集加入再用刚才得到的边集跑最小生成树就行了
接下来需要计算每个新边的边权最大值
对于一棵生成树
我们可以用这个性质来约束边权
对于
于是这个可以用一个 lca 搞定,当然这里只有
最后还要计算每条边有多少人通过,这个用一遍 dfs 求子树大小就可以求出来了
代码比较烦,但是细节还是比较少的 (所以为什么我一个 ; 写成 , 还能得 35pts 啊)
时间复杂度
code:
#include<bits/stdc++.h>
#define MAXN 300010
#define MAXK 30
#define int long long
using namespace std;
int n,m,K,tot,a[MAXN];
struct Node{int fr,to,val;}e[MAXN],ne[MAXN],que[MAXN];
bool cmp(Node a,Node b){return a.val<b.val;}
int f[MAXN],g[MAXN];
int getf(int x){return f[x]==x?x:f[x]=getf(f[x]);}
int getg(int x){return g[x]==x?x:g[x]=getg(g[x]);}
int col[MAXN],cnt,val[MAXK];
void kruskal(){
for(int i=1;i<=n;i++)f[i]=g[i]=i;
sort(e+1,e+m+1,cmp);
for(int i=1;i<=K;i++){
int u=ne[i].fr,v=ne[i].to;
int a=getf(u),b=getf(v);
if(a==b)continue;
f[a]=b;
}
for(int i=1;i<=m;i++){
int u=e[i].fr,v=e[i].to;
int a=getf(u),b=getf(v);
if(a==b)continue;
f[a]=b;
a=getg(u),b=getg(v);
if(a<b)swap(a,b);g[a]=b;
}
for(int i=1;i<=n;i++){
if(getg(i)==i)col[i]=++cnt,val[cnt]=a[i];
else col[i]=col[getg(i)],val[col[i]]+=a[i];
}
for(int i=1;i<=K;i++){
int u=ne[i].fr,v=ne[i].to;
ne[i]=(Node){col[u],col[v],0};
}
for(int i=1;i<=m;i++){
int u=e[i].fr,v=e[i].to,w=e[i].val;
int a=getg(u),b=getg(v);
if(a==b)continue;
que[++tot]=(Node){col[u],col[v],w};
if(a<b)swap(a,b);g[a]=b;
}
}
struct tnode{int to,nxt;}Edge[MAXK<<1];
int cnt_Edge,Head[MAXK];
void Add_Edge(int u,int v){
Edge[++cnt_Edge]=(tnode){v,Head[u]};
Head[u]=cnt_Edge;
}
int fa[MAXK],dep[MAXK],mn[MAXK],sz[MAXK];
void dfs(int u,int fat){
fa[u]=fat;dep[u]=dep[fat]+1;sz[u]=val[u];
for(int i=Head[u];i;i=Edge[i].nxt){
int v=Edge[i].to;if(v==fat)continue;
dfs(v,u);sz[u]+=sz[v];
}
}
void update(int u,int v,int w){
while(u!=v){
if(dep[u]>dep[v])swap(u,v);
mn[v]=min(mn[v],w);v=fa[v];
}
}
signed main(){
scanf("%lld%lld%lld",&n,&m,&K);
for(int i=1;i<=m;i++){
int u,v,w;scanf("%lld%lld%lld",&u,&v,&w);
e[i]=(Node){u,v,w};
}
for(int i=1;i<=K;i++){
int u,v;scanf("%lld%lld",&u,&v);
ne[i]=(Node){u,v,0};
}
for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
kruskal();int ans=0,rt=col[1];
for(int s=0;s<(1<<K);s++){
memset(Head,0,sizeof(Head));cnt_Edge=0;
memset(fa,0,sizeof(fa));memset(dep,0,sizeof(dep));
memset(mn,0x3f,sizeof(mn));memset(sz,0,sizeof(sz));
for(int i=1;i<=cnt;i++)f[i]=i;bool fl=false;
for(int i=1;i<=K;i++){
if(!((s>>(i-1))&1))continue;
int u=ne[i].fr,v=ne[i].to;
int a=getf(u),b=getf(v);
if(a==b){fl=true;break;}
f[a]=b;Add_Edge(u,v);Add_Edge(v,u);
}if(fl)continue;
for(int i=1;i<=tot;i++){
int u=que[i].fr,v=que[i].to;
int a=getf(u),b=getf(v);
if(a==b)continue;
f[a]=b;Add_Edge(u,v);Add_Edge(v,u);
}dfs(rt,0);
for(int i=1;i<=tot;i++){
int u=que[i].fr,v=que[i].to,w=que[i].val;
update(u,v,w);
}int res=0;
for(int i=1;i<=K;i++){
if(!((s>>(i-1))&1))continue;
int u=ne[i].fr,v=ne[i].to;
if(dep[u]<dep[v])swap(u,v);
res+=mn[u]*sz[u];
}ans=max(ans,res);
}printf("%lld",ans);
return 0;
}