您的位置:首页 > 其它

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

2018-04-11 17:20 651 查看

3206: [Apio2013]道路费用

 

Time Limit: 20 Sec  Memory Limit: 128 MB
Submit: 536  Solved: 252
[Submit][Status][Discuss]

Description

 

 

Input

 

第一行包含三个由空格隔开的整数N,M和K。 接下来的 M行描述最开始的M 条道路 这M行中的第i行包含由空格隔开的整数ai,bi和c i,表示有一条在a i和b i之间,费用为c i的双向道路。 接下来的K行描述新建的K条道路。 这 K行中的第i行包含由空格隔开的整数 xi和yi,表示有一条连接城镇xi和yi新道路 最后一行包含N个由空格隔开的整数,其中的第j个为pj,表示从城镇j 前往城镇 1的人数。 输入也满足以下约束条件。 1 ≤ N ≤ 100000;1 ≤ K ≤ 20;1 ≤ M ≤ 300000;对每个i和j,1 ≤ ci, pj ≤ 10^6; 注意:边权值可能相同

Output

 

你的程序必须输出恰好一个整数到标准输出,表示能获得的最大的收入。

Sample Input

5 5 1
3 5 2
1 2 3
2 3 5
2 4 4
4 3 6
1 3
10 20 30 40 50

 

 

Sample Output

 

400

 

HINT

 

 


在样例中, Mr. Greedy应该将新道路(1,3)的费用设置为 5分钱。
在这个费用下,他可以选择道路(3,5),(1,2),(2,4)和(1,3)来最小化总费用,这个费用为14。
从城镇 3出发的 30个人和从城镇 5出发的 50个人将经过新道路前往城镇 1,因此他可以获得为(30+50)_5=400 分钱的最好收入。
如果我们这样做,将新道路(1,3)的费用设置为 10分钱。
根据传统的限制,Mr. Greedy必须选择(3,5),(1,2),(2,4)和(2,3),因为这是唯一费用最小的集合
。因此,在嘉年华的过程中道路(1,3)将没有任何收入。

 

Source

 

 

分析:

一开始有K条特殊边。我们可以二进制枚举哪些选哪些不选。

先把这K条特殊边权值赋-inf跑最小生成树,除了k条边以外其他边肯定在任何枚举中都会选。

那么把那些边缩点,最后剩下k个点。

我们再找出这k个点中可能选的边,最多k^2条。

这样二进制枚举加每次做最小生成树代价是2^k *(k ^ 2)

再dfs一编新树,把没有选的边u - v的路径上所以点暴力取最小值,这样每个点取得最小值记录为mx[i]。

每个点子树内权值和记录为w[i]

记录每个特殊边深度深的点为x

最终答案为∑mx[x] * w[x]。

AC代码:

 

# include <iostream>
# include <cstdio>
# include <cstring>
# include <algorithm>
using namespace std;
typedef long long LL;
const int N = 1e5 + 12;
const int M = 4e5 + 12;
const LL inf = 1e18;
int n,m,K,dt,bac
,base
,cnt,tot,fa
;
LL s
,S
,mx
,w
,ans;int head
,dep
,rt;
struct Base{
int fa
;
void init(int x){for(int i = 1;i <= x;i++)fa[i] = i;}
int find(int x){return x == fa[x] ? x : fa[x] = find(fa[x]);}
}p,q;
struct E{
int u,v;LL w;
void read(bool f){scanf("%d %d",&u,&v);if(f)scanf("%lld",&w);}
}a[M],b[52],e[252];
struct Edge{
int to,nex;
}edge
;
void AddEdge(int u,int v)
{
edge[++tot] = (Edge){v,head[u]};
head[u] = tot;
edge[++tot] = (Edge){u,head[v]};
head[v] = tot;
}
bool cmp(E a,E b){return a.w < b.w;}
void dfs(int u)
{
w[u] = s[u];
for(int i = head[u];i;i = edge[i].nex)
{
if(edge[i].to == fa[u])continue;
fa[edge[i].to] = u;
dep[edge[i].to] = dep[u] + 1;
dfs(edge[i].to);
w[u] += w[edge[i].to];
}
}
void solve(int sa)
{
tot = 0;
for(int i = 1;i <= dt;i++)
{
mx[base[i]] = inf;fa[base[i]] = head[base[i]] = 0;
p.fa[base[i]] = base[i];
}
for(int i = 0;i < K;i++)if(sa >> i & 1)
{
int x = p.find(b[i].u),y = p.find(b[i].v);
if(x == y)return;
p.fa[y] = x;AddEdge(b[i].u,b[i].v);
}
for(int i = 0;i < cnt;i++)
{
int x = p.find(e[i].u),y = p.find(e[i].v);
if(x != y)p.fa[y] = x,AddEdge(e[i].u,e[i].v);
}
dfs(rt);
for(int i = 0;i < cnt;i++)
{
int x = e[i].u,y = e[i].v;
if(dep[x] < dep[y])swap(x,y);
while(dep[x] != dep[y])
{
mx[x] = min(mx[x],e[i].w);
x = fa[x];
}
while(x != y)
{
mx[x] = min(mx[x],e[i].w);
mx[y] = min(mx[y],e[i].w);
x = fa[x];y = fa[y];
}
}
LL ret = 0;
for(int i = 0;i < K;i++)if(sa >> i & 1)
{
int x = b[i].u,y = b[i].v;
if(dep[x] < dep[y])swap(x,y);
ret += mx[x] * w[x];
}
ans = max(ans,ret);
}
int main()
{
freopen("TOLL.in","r",stdin);
freopen("TOLL.out","w",stdout);
scanf("%d %d %d",&n,&m,&K);
p.init(n);q.init(n);
for(int i = 0;i < m;i++)a[i].read(1);
for(int i = 0;i < K;i++)b[i].read(0);
for(int i = 1;i <= n;i++)scanf("%lld",S + i);
sort(a,a + m,cmp);
for(int i = 0;i < K;i++)
{
int x = p.find(b[i].u),y = p.find(b[i].v);
if(x != y)p.fa[y] = x;
}
for(int i = 0;i < m;i++)
{
int x = p.find(a[i].u),y = p.find(a[i].v);
if(x != y)
{
p.fa[y] = x;
x = q.find(a[i].u),y = q.find(a[i].v);
q.fa[y] = x;
}
}
for(int i = 1;i <= n;i++)if(q.find(i) == i)base[++dt] = i,bac[i] = i;
for(int i = 1;i <= n;i++)bac[i] = bac[q.find(i)],s[bac[i]] += S[i];
rt = bac[1];
for(int i = 0;i < m;i++)a[i].u = bac[a[i].u],a[i].v = bac[a[i].v];
for(int i = 0;i < K;i++)b[i].u = bac[b[i].u],b[i].v = bac[b[i].v];
for(int i = 0;i < m;i++)
{
int x = q.find(a[i].u),y = q.find(a[i].v);
if(x != y)
{
q.fa[y] = x;
e[cnt++] = a[i];
}
}
int all = 1 << K;
for(int i = 1;i < all;i++)solve(i);
printf("%lld\n",ans);
fclose(stdin);
fclose(stdout);
return 0;
}

 

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: