首先我们强制要求几条待定价的边在MST中,建出MST

我们发现这个MST中原来的边是一定要被选上的,所以可以把点缩起来,搞成一棵只有$K$个点的树

然后$2^K$枚举每条边在不在最终的MST中,让在最终MST中的待定价的边尽量大,只需要在Kruskal的时候暴力更新每条边的定价即可

时间复杂度$O(m * logm + 2^K * K^2)$

 /**************************************************************
Problem: 3206
User: rausen
Language: C++
Result: Accepted
Time:8040 ms
Memory:8232 kb
****************************************************************/ #include <cstdio>
#include <algorithm> using namespace std;
typedef long long ll;
const int N = 1e5 + ;
const int M = 3e5 + ;
const int K = ;
const int inf = 1e9; inline int read(); struct Edge {
int x, y, v; inline void get(int f) {
x = read(), y = read();
if (f) v = read();
} inline bool operator < (const Edge &E) const {
return v < E.v;
}
} E[M], Ek[K], s[K]; struct edge {
int next, to;
edge() {}
edge(int _n, int _t) : next(_n), to(_t) {}
} e[K << ]; int first[N], tot; struct tree_node {
int fa, dep, mn;
ll v, sum;
} tr[N]; int n, m, k, S, top;
int fa[][N];
int root[K], cnt_root;
int u[K];
ll ans; inline void Add_Edges(int x, int y) {
e[++tot] = edge(first[x], y), first[x] = tot;
e[++tot] = edge(first[y], x), first[y] = tot;
} int find(int x, int f) {
return x == fa[f][x] ? x : fa[f][x] = find(fa[f][x], f);
} #define y e[x].to
void dp(int p) {
int x;
tr[p].sum = tr[p].v;
for (x = first[p]; x; x = e[x].next)
if (y != tr[p].fa) {
tr[y].dep = tr[p].dep + , tr[y].fa = p;
dp(y);
tr[p].sum += tr[y].sum;
}
}
#undef y ll work() {
static int i, x, y, p;
static ll res;
for (tot = , i = ; i <= k + ; ++i) {
p = root[i];
fa[][p] = p;
first[p] = tr[p].fa = , tr[p].mn = inf;
}
for (i = ; i <= k; ++i)
if (u[i]) {
x = find(Ek[i].x, ), y = find(Ek[i].y, );
if (x == y) return ;
fa[][x] = y;
Add_Edges(Ek[i].x, Ek[i].y);
}
for (i = ; i <= k; ++i) {
x = find(s[i].x, ), y = find(s[i].y, );
if (x != y) fa[][x] = y, Add_Edges(s[i].x, s[i].y);
}
dp(S);
for (i = ; i <= k; ++i) {
x = s[i].x, y = s[i].y;
if (tr[x].dep < tr[y].dep) swap(x, y);
while (tr[x].dep != tr[y].dep)
tr[x].mn = min(tr[x].mn, s[i].v), x = tr[x].fa;
while (x != y) {
tr[x].mn = min(tr[x].mn, s[i].v);
tr[y].mn = min(tr[y].mn, s[i].v);
x = tr[x].fa, y = tr[y].fa;
}
}
#define x Ek[i].x
#define y Ek[i].y
for (res = , i = ; i <= k; ++i)
if (u[i])
res += tr[x].dep > tr[y].dep ? tr[x].mn * tr[x].sum : tr[y].mn * tr[y].sum;
#undef x
#undef y
return res;
} void dfs(int p) {
if (p == k + ) {
ans = max(ans, work());
return;
}
u[p] = , dfs(p + );
u[p] = , dfs(p + );
} int main() {
int i, x, y;
n = read(), m = read(), k = read();
for (i = ; i <= m; ++i) E[i].get();
for (i = ; i <= k; ++i) Ek[i].get();
for (i = ; i <= n; ++i) tr[i].v = read();
sort(E + , E + m + );
for (i = ; i <= n; ++i) fa[][i] = fa[][i] = i; for (i = ; i <= k; ++i)
fa[][find(Ek[i].x, )] = find(Ek[i].y, );
#define x E[i].x
#define y E[i].y
for (i = ; i <= m; ++i)
if (find(x, ) != find(y, ))
fa[][find(x, )] = fa[][find(y, )], fa[][find(x, )] = fa[][find(y, )];
#undef x
#undef y
S = find(, );
for (i = ; i <= n; ++i)
if (find(i, ) != i) tr[find(i, )].v += tr[i].v;
else root[++cnt_root] = i;
#define x Ek[i].x
#define y Ek[i].y
for (i = ; i <= k; ++i)
x = find(x, ), y = find(y, );
#undef x
#undef y
#define x E[i].x
#define y E[i].y
for (i = ; i <= m; ++i)
x = find(x, ), y = find(y, );
for (i = ; i <= m; ++i)
if (find(x, ) != find(y, ))
s[++top] = E[i], fa[][find(x, )] = find(y, );
#undef x
#undef y
dfs();
printf("%lld\n", ans);
return ;
} inline int read() {
static int x;
static char ch;
x = , ch = getchar();
while (ch < '' || '' < ch)
ch = getchar();
while ('' <= ch && ch <= '') {
x = x * + ch - '';
ch = getchar();
}
return x;
}

BZOJ3206 [Apio2013]道路费用的更多相关文章

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

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

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

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

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

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

  4. [APIO2013]道路费用

    题目描述 幸福国度可以用 N 个城镇(用 1 到 N 编号)构成的集合来描述,这些城镇 最开始由 M 条双向道路(用 1 到 M 编号)连接.城镇 1 是中央城镇.保证一个 人从城镇 1 出发,经过这 ...

  5. 题解 [APIO2013]道路费用

    link Description 幸福国度可以用 N 个城镇(用 1 到 N 编号)构成的集合来描述,这些城镇 最开始由 M 条双向道路(用 1 到 M 编号)连接.城镇 1 是中央城镇.保证一个 人 ...

  6. bzoj 3206: [Apio2013]道路费用【最小生成树+并查集】

    参考:http://hzwer.com/6888.html 把k条道路权值设为0,和其他边一起跑MST,然后把此时选中的其他边设为必选,在新图中加上必选变缩成k个点,把所有边重标号,枚举k跳边的选取情 ...

  7. 题解 洛谷 P3639 【[APIO2013]道路费用 】

    不难想到可以\(2^k\)去枚举\(k\)条新边的选择方案,然后加入原图中的边来使图连通,用当前方案的收益去更新答案,但是这样复杂度过不去. 可以先把\(k\)条新边都连上,然后再加入边权从小到大排序 ...

  8. PKUSC2018训练日程(4.18~5.30)

    (总计:共66题) 4.18~4.25:19题 4.26~5.2:17题 5.3~5.9: 6题 5.10~5.16: 6题 5.17~5.23: 9题 5.24~5.30: 9题 4.18 [BZO ...

  9. [APIO2013]

    A.机器人 题目大意:给定一个n*m的地图,有一些障碍物和k个机器人,你每次可以选择一个机器人往任意一个方向推,遇到转向器会转向,两个编号相邻的机器人可以合并,求最少推多少次可以全部合并. $n,m\ ...

随机推荐

  1. 即时定位与地图构建SLAM(Simultaneous Localization and Mapping)

    SLAM 即时定位与地图构建SLAM(Simultaneous Localization and Mapping) 参考链接: 视觉SLAM漫谈,http://www.cnblogs.com/gaox ...

  2. python_way ,day1 编译安装python3、基础及流程控制

    本节内容: 1,Python介绍发展史 2,安装 3,Hello World 4,程序 5,变量,字符编码 6,用户输入 7,模块初识 一.python介绍 python的创始人为吉多·范罗苏姆(Gu ...

  3. C#强制清除缓存

    #region 强制清除缓存 [DllImport("kernel32.dll", EntryPoint = "SetProcessWorkingSetSize" ...

  4. STL--list

    List-概述: 列表List是一个线性链表结构(Double—Linked Lists,双链表),它的数据由若干个节点构成,每一个节点都包括一个信息块Info(即实际存储的数据).一个前驱指针Pre ...

  5. SecureCRT显示中文和语法高亮

    因为默认情况下,SecureCRT不能显示语法高亮特性,整个界面颜色单一,看起来不爽,也没有效率,所有通过设置一下语法高亮还是很有必要的, 默认字体也看着不是很清晰,还是更改为我比较喜欢的Courie ...

  6. Web Service 接口实现大量数据传输的解决方案

    1,流程图 2,流程说明:线程1开始请求接口获取1W条数据,当数据成功获取后,接口是闲置的,这时我们开始线程2获取数据,同时线程1继续执行获取数据的后续工作,如果转换数据,这里我用的办法是,使用预先定 ...

  7. HDU1542矩形面积并

    取出纵向边按x坐标排序,在y方向上建立线段树. 每次查询当前有效长度len,ans += len*(x[i]-x[i-1]); 其中len为T[rt].len; 查询完毕后更新y方向上线段树,入边+1 ...

  8. Oracle数据库高级查询(五)集合查询

    现实需求有时候需要将多个查询组合到一个查询中去 这时就需要使用集合查询操作了 这个操作类似于数学中的交集,并集,和补集的操作   交集就是返回两个查询共有的记录,关键字是INTERSECT 并集是返回 ...

  9. Mysql插入数据的时候,中文乱码问题的解决

    如果在Mysql中插入数据的时候,没有特定指定编码,可能会产生一系列的问题,例如,如果用insert语句的时候,可能提示incorrect values,等...究其原因,实际上无非是要让数据库和表中 ...

  10. (六)C语言之typedef详解

    1.typedef可以看作type define的缩写,顾名思义就是类型定义,也就是说它只是给已有的类型重新定义了一个方便使用的别名,并没有产生新的数据类型.typedef的使用与宏定义define有 ...