不难想到可以\(2^k\)去枚举\(k\)条新边的选择方案,然后加入原图中的边来使图连通,用当前方案的收益去更新答案,但是这样复杂度过不去。

可以先把\(k\)条新边都连上,然后再加入边权从小到大排序过后的原图的边,直到图连通。后加入的原图的边在任何一个新边的选择方案都是要加入的,因为找这些边时是选了所有\(k\)条新边,其他方案只会比这时选择更少的新边,所以为保证连通,这些后加入的边肯定是要选择的,可能还要加入更多的原图中的边,同时这些边是按边权排序后的,所以也能满足题目中最小生成树的要求。

根据原图中边权互不相同,所以这些后加入的边的集合是唯一的。因为这些后加入的边是必选的,所以可以把只考虑这些边的连通块缩成点,发现缩点后的数量最多为\(k+1\)。

上面也说到,可能在一个新边的选择方案中,还需加入更多的原图中的边,这些边就是使这\(k+1\)个点连通的边,把这\(k\)条边记录下来,作为处理选择方案时的备选边。

然后就可以按之前的做法来处理了,\(2^k\)去枚举\(k\)条新边的选择方案,然后加入这\(k\)条备选边来使图连通,然后用当前方案的收益去更新答案。

具体实现加边判断连通性和缩点时用并查集比较方便。

总复杂度为\(O(m \log m + 2^k k^2)\)。

\(code:\)

#include<bits/stdc++.h>
#define maxn 3000010
#define inf 1000000000
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,m,k,root,cnt,tot;
ll ans,sum;
int id[maxn],fa[maxn],de[maxn];
ll p[maxn],pe[maxn],siz[maxn],mi[maxn];
bool flag;
struct edge
{
int to,nxt;
}e[maxn];
int head[maxn],edge_cnt;
void add(int from,int to)
{
e[++edge_cnt]=(edge){to,head[from]};
head[from]=edge_cnt;
}
struct Edge
{
int x,y,v;
}e1[maxn],e2[maxn],e3[maxn];
bool cmp(const Edge &a,const Edge &b)
{
return a.v<b.v;
}
struct node
{
int f[maxn];
int find(int x)
{
return f[x]==x?x:f[x]=find(f[x]);
}
void merge(int x,int y)
{
x=find(x),y=find(y);
if(x==y) return;
f[x]=y;
}
}A,B;
void dfs(int x,int fath)
{
fa[x]=fath,de[x]=de[fath]+1,siz[x]=pe[x];
for(int i=head[x];i;i=e[i].nxt)
{
int y=e[i].to;
if(y==fath) continue;
dfs(y,x),siz[x]+=siz[y];
}
}
int main()
{
read(n),read(m),read(k);
for(int i=1;i<=n;++i) A.f[i]=B.f[i]=i;
for(int i=1;i<=m;++i)
read(e1[i].x),read(e1[i].y),read(e1[i].v);
sort(e1+1,e1+m+1,cmp);
for(int i=1;i<=k;++i)
read(e2[i].x),read(e2[i].y);
for(int i=1;i<=n;++i) read(p[i]);
for(int i=1;i<=k;++i) A.merge(e2[i].x,e2[i].y);
for(int i=1;i<=m;++i)
{
int x=e1[i].x,y=e1[i].y;
if(A.find(x)==A.find(y)) continue;
A.merge(x,y),B.merge(x,y);
}
for(int i=1;i<=n;++i)
if(B.find(i)==i)
id[i]=++tot;
root=id[B.find(1)],A=B;
for(int i=1;i<=n;++i) pe[id[B.find(i)]]+=p[i];
for(int i=1;i<=m;++i)
{
int x=e1[i].x,y=e1[i].y;
if(B.find(x)==B.find(y)) continue;
B.merge(x,y),e3[++cnt]=e1[i];
}
for(int i=1;i<=k;++i) e2[i].x=id[A.find(e2[i].x)],e2[i].y=id[A.find(e2[i].y)];
for(int i=1;i<=cnt;++i) e3[i].x=id[A.find(e3[i].x)],e3[i].y=id[A.find(e3[i].y)];
for(int S=0;S<(1<<k);++S)
{
edge_cnt=sum=flag=0;
for(int i=1;i<=tot;++i) A.f[i]=i,head[i]=0,mi[i]=inf;
for(int i=1;i<=k;++i)
{
if(!(S&(1<<(i-1)))) continue;
int x=e2[i].x,y=e2[i].y;
if(A.find(x)==A.find(y))
{
flag=true;
break;
}
A.merge(x,y),add(x,y),add(y,x);
}
if(flag) continue;
for(int i=1;i<=cnt;++i)
{
int x=e3[i].x,y=e3[i].y;
if(A.find(x)==A.find(y)) continue;
A.merge(x,y),add(x,y),add(y,x);
}
dfs(root,0);
for(int i=1;i<=cnt;++i)
{
int x=e3[i].x,y=e3[i].y;
ll v=e3[i].v;
while(x!=y)
{
if(de[x]<de[y]) swap(x,y);
mi[x]=min(mi[x],v),x=fa[x];
}
}
for(int i=1;i<=k;++i)
{
if(!(S&(1<<(i-1)))) continue;
int x=e2[i].x,y=e2[i].y;
if(de[x]<de[y]) swap(x,y);
sum+=mi[x]*siz[x];
}
ans=max(ans,sum);
}
printf("%lld",ans);
return 0;
}

题解 洛谷 P3639 【[APIO2013]道路费用 】的更多相关文章

  1. 洛谷P3639 [APIO2013] 道路费用 [生成树的特殊算法]

    题目传送门 道路费用 格式难调,题面就不放了. 分析: 这是一道要细(yan)心(jing)的生成树的好(gui)题. 首先我们看到$k$的范围非常小,那么我们就可以直接$2^k$枚举每一条加边是否选 ...

  2. 洛谷 P5019 铺设道路

    题目描述 春春是一名道路工程师,负责铺设一条长度为 \(n\) 的道路. 铺设道路的主要工作是填平下陷的地表.整段道路可以看作是 \(n\) 块首尾相连的区域,一开始,第 \(i\) 块区域下陷的深度 ...

  3. [BZOJ3206][APIO2013]道路费用(最小生成树)

    3206: [Apio2013]道路费用 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 568  Solved: 266[Submit][Status ...

  4. [Bzoj3206][Apio2013]道路费用(kruscal)(缩点)

    3206: [Apio2013]道路费用 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 536  Solved: 252[Submit][Status ...

  5. 题解 洛谷P5018【对称二叉树】(noip2018T4)

    \(noip2018\) \(T4\)题解 其实呢,我是觉得这题比\(T3\)水到不知道哪里去了 毕竟我比较菜,不大会\(dp\) 好了开始讲正事 这题其实考察的其实就是选手对D(大)F(法)S(师) ...

  6. 题解 洛谷 P3396 【哈希冲突】(根号分治)

    根号分治 前言 本题是一道讲解根号分治思想的论文题(然鹅我并没有找到论文),正 如论文中所说,根号算法--不仅是分块,根号分治利用的思想和分块像 似却又不同,某一篇洛谷日报中说过,分块算法实质上是一种 ...

  7. 题解-洛谷P5410 【模板】扩展 KMP(Z 函数)

    题面 洛谷P5410 [模板]扩展 KMP(Z 函数) 给定两个字符串 \(a,b\),要求出两个数组:\(b\) 的 \(z\) 函数数组 \(z\).\(b\) 与 \(a\) 的每一个后缀的 L ...

  8. 题解-洛谷P4229 某位歌姬的故事

    题面 洛谷P4229 某位歌姬的故事 \(T\) 组测试数据.有 \(n\) 个音节,每个音节 \(h_i\in[1,A]\),还有 \(m\) 个限制 \((l_i,r_i,g_i)\) 表示 \( ...

  9. 题解-洛谷P4724 【模板】三维凸包

    洛谷P4724 [模板]三维凸包 给出空间中 \(n\) 个点 \(p_i\),求凸包表面积. 数据范围:\(1\le n\le 2000\). 这篇题解因为是世界上最逊的人写的,所以也会有求凸包体积 ...

随机推荐

  1. 云计算课程实验之安装Hadoop及配置伪分布式模式的Hadoop

    一.实验目的 1. 掌握Linux虚拟机的安装方法. 2. 掌握Hadoop的伪分布式安装方法. 二.实验内容 (一)Linux基本操作命令 Linux常用基本命令包括: ls,cd,mkdir,rm ...

  2. 深入理解RocketMQ(九)---实战(控制台搭建)

    rocketMQ控制台搭建 (1)下载rocketmq-console代码:https://github.com/875279177/incubator-rocketmq-externals (2)修 ...

  3. 学习Java的Day03

    接口的特点!!!! 接口不能创建对象 接口的变量使用public static final修饰,如果不写默认添加: 接口的方法为public abstrict,如果不写默认添加: 子类必须重写接口中所 ...

  4. java中的四种访问权限是?

    1.私有权限(private) private可以修饰数据成员,构造方法,方法成员,不能修饰类(此处指外部类,不考虑内部类).被private修饰的成员,只能在定义它们的类中使用,在其他类中不能调用. ...

  5. 题解 P1144 【最短路计数】

    这道题用一次SPFA就可以过了.在求最短路的同时,对答案进行统计即可. 实现: \(dis_i\)表示从1到\(i\)的最短路(实在还是不懂的话看程序吧). 当\(dis_i>dis_j+1\) ...

  6. web开发相关概念

    什么是web通信? WEB采用B/S通信模式,通过超文本传送协议(HTTP, Hypertext transport protocol)进行通信.通过浏览器地址栏编写URL,向服务器发送一个请求,服务 ...

  7. 探讨NET Core数据进行3DES加密或解密弱密钥问题

    前言 之前写过一篇<探讨.NET Core数据进行3DES加密和解密问题>,最近看到有人提出弱密钥问题,换个强密钥不就完了吗,猜测可能是与第三方对接导致很无奈不能更换密钥,所以产生本文解决 ...

  8. 【Java8新特性】冰河带你看尽Java8新特性,你想要的都在这儿了!!(文本有福利)

    写在前面 很多小伙伴留言说,冰河你能不能写一些关于Java8的文章呢,看书看不下去,看视频进度太慢.好吧,看到不少读者对Java8还是比较陌生的,那我就写一些关于Java8的文章吧,希望对大家有所帮助 ...

  9. SpringCloud系列之集成分布式事务Seata应用篇

    目录 前言 项目版本 项目说明 Seata服务端部署 Seata客户端集成 cloud-web module-order module-cart module-goods module-wallet ...

  10. 在react中使用到的好用的插件

    1)antd UI组件 蚂蚁金服出品的 挺好用的 还有个移动端的antd-moblie 2) moment 日期处理类库 3)prop-types 第三方库 对组件props变量进行类型检测 4)qs ...