P3639-[APIO2013]道路费用【最小生成树】

1|0正题

题目链接:https://www.luogu.com.cn/problem/P3639


1|1题目大意

给出n个点m条有边权的无向图,然后再给出k条边权未定义的边,然后每个点有一个人数pi

现在要你给未确定的边权的边确定边权然后选出图的一棵最小生成树,之后所有点上的人都从自己的点走到根节点,当一个人经过刚刚确定边权的边时会支付这条边的权值的费用,现在要求总费用和最大。

保证m条边的图联通且权值互不相同。

1n105,1m3×105,1k20


1|2解题思路

突破口肯定在于权值互不相同,因为这样的话最小生成树就唯一了,然后发现我们加上k条边后最多替换掉原来图上的k条边,所以大部分的边都是和原来的相同的。

我们可以先把这k条边连接上,然后跑一棵最小生成树,再吧这k条边去掉这样就最多会产生k+1个连通块。

然后再用联通块跑一次最小生成树,把这些边记下来,这些边是可能使用上的。

之后我们2k枚举哪些边选不选入最小生成树上,然后拿上面的边跑最小生成树,之后每条边的权值就是所有连接分割的两个联通块的最小边权,这个我们可以枚举边然后直接暴力跳,之后统计答案就好了。

时间复杂度:O(mlogm+2kk2)


1|3code

#include<cstdio> #include<cstring> #include<algorithm> #include<vector> #define ll long long using namespace std; const ll N=3e5+10,K=22; struct edge{ ll x,y,w; }e[N]; ll n,m,k,cnt,answer,p[N],fa[N],fb[N],rev[N]; ll r[K],w[K],dep[K],f[K],dx[K],dy[K],mn[K]; vector<int>G[K];ll a[K][K]; bool cmp(edge x,edge y) {return x.w<y.w;} ll find(ll x) {return (fa[x]==x)?x:(fa[x]=find(fa[x]));} ll finb(ll x) {return (fb[x]==x)?x:(fb[x]=finb(fb[x]));} void dfs(ll x,ll fa){ r[x]=w[x];f[x]=fa;dep[x]=dep[fa]+1; for(ll i=0;i<G[x].size();i++){ ll y=G[x][i]; if(y==fa)continue; dfs(y,x);r[x]+=r[y]; } return; } signed main() { scanf("%lld%lld%lld",&n,&m,&k); for(ll i=1;i<=m;i++){ ll x,y,w; scanf("%lld%lld%lld",&x,&y,&w); e[i]=(edge){x,y,w}; } sort(e+1,e+1+m,cmp); for(ll i=1;i<=n;i++)fa[i]=fb[i]=i; for(ll i=0;i<k;i++){ scanf("%lld%lld",&dx[i],&dy[i]); ll x=find(dx[i]),y=find(dy[i]); if(x==y)continue;fa[x]=y; } for(ll i=1;i<=n;i++)scanf("%lld",&p[i]); for(ll i=1;i<=m;i++){ ll x=e[i].x,y=e[i].y; x=find(x);y=find(y); if(x==y)continue;fa[x]=y; fb[finb(e[i].x)]=finb(e[i].y); } for(ll i=1;i<=n;i++) if(finb(i)==i)rev[i]=++cnt,fa[cnt]=cnt; for(ll i=1;i<=n;i++)rev[i]=rev[finb(i)]; for(ll i=1;i<=n;i++)w[rev[i]]+=p[i]; memset(a,0x3f,sizeof(a));int pm=m;m=0; for(ll i=1;i<=pm;i++){ ll x=e[i].x,y=e[i].y,w=e[i].w; x=rev[x];y=rev[y]; if(find(x)==find(y))continue; fa[find(x)]=find(y); e[++m]=(edge){x,y,w}; } sort(e+1,e+1+m,cmp); ll MS=(1<<k); for(ll i=0;i<k;i++)dx[i]=rev[dx[i]],dy[i]=rev[dy[i]]; for(ll s=0;s<MS;s++){ memset(mn,0x3f,sizeof(mn)); for(ll i=1;i<=cnt;i++) fa[i]=i,G[i].clear(); bool flag=0; for(ll i=0;i<k;i++){ if(!((s>>i)&1))continue; ll x=find(dx[i]),y=find(dy[i]); if(x==y){flag=1;break;}fa[x]=y; G[dx[i]].push_back(dy[i]); G[dy[i]].push_back(dx[i]); } if(flag)continue; for(ll i=1;i<=m;i++){ ll x=find(e[i].x),y=find(e[i].y); if(x==y)continue;fa[x]=y; G[e[i].x].push_back(e[i].y); G[e[i].y].push_back(e[i].x); } dfs(rev[1],0); for(ll i=1;i<=m;i++){ ll x=e[i].x,y=e[i].y; while(x!=y){ if(dep[x]<dep[y])swap(x,y); mn[x]=min(mn[x],e[i].w);x=f[x]; } } ll ans=0; for(ll i=0;i<k;i++){ if(!((s>>i)&1))continue; ll x=dx[i],y=dy[i]; if(dep[x]<dep[y])swap(x,y); ans+=mn[x]*r[x]; } answer=max(answer,ans); } printf("%lld\n",answer); return 0; }

__EOF__

本文作者QuantAsk
本文链接https://www.cnblogs.com/QuantAsk/p/15430226.html
关于博主:退役OIer,GD划水选手
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   QuantAsk  阅读(49)  评论(0编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示