codeforces1023F Mobile Phone Network

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yzyyylx/article/details/87941465

题面

题意

有n个点,并给出k条还没有权值的边和m条有权值的边,要求你给这k条边赋上权值,使得存在一种最小生成树包含这k条边,问k条边的最大权 值和是多少。

做法

首先可以先求出这棵最小生成树,然后这m条边中的所有非树边都会与原树构成一个环,要满足它不是树边,它就必须是环上的最大值,相当于要对环上的所有树边都取一次最小值,这个用树链剖分,差分+启发式合并或可并堆…都可以做,但都很麻烦。
可以发现,如果将所有操作的值排序(输入其实已经排好序了),这样每次操作赋的值是递增的,也就是说一条边一旦被赋值就不会再被修改了,为了防止重复赋值,一旦一条边的权值已经确定,就将它连接的父子用并查集合并,这样每条边只会被扫到一次,总时间复杂度仅为 O ( n ) O(n)

代码

#include<bits/stdc++.h>
#define ll long long
#define N 500100
using namespace std;

ll n,K,m,sum,bcj[N],ans[N],a[N],b[N],c[N],deep[N],fa[N];
bool tree[N],need[N];
vector<ll>to[N],tr[N];

ll ff(ll u){return u==bcj[u]?u:bcj[u]=ff(bcj[u]);}
void dfs(ll now,ll last)
{
    ll i,t;
    for(i=0;i<to[now].size();i++)
    {
	t=to[now][i];
	if(t==last) continue;
	deep[t]=deep[now]+1;
	need[t]=tr[now][i];
	fa[t]=now;
	dfs(t,now);
    }
}

int main()
{
    ll i,j,p,q;
    cin>>n>>K>>m;
    for(i=1;i<=n;i++) bcj[i]=i;
    for(i=1;i<=K;i++)
    {
	scanf("%lld%lld",&p,&q);
	bcj[ff(p)]=ff(q);
	to[p].push_back(q),to[q].push_back(p);
	tr[p].push_back(1),tr[q].push_back(1);
    }
    for(i=1;i<=m;i++)
    {
	scanf("%lld%lld%lld",&p,&q,&c[i]);
	a[i]=p,b[i]=q;
	if(ff(p)==ff(q)) continue;
	tree[i]=1;
	bcj[ff(p)]=ff(q);
	to[p].push_back(q),to[q].push_back(p);
	tr[p].push_back(0),tr[q].push_back(0);
    }
    deep[1]=1;
    dfs(1,-1);
    for(i=1;i<=n;i++) bcj[i]=i;
    for(i=1;i<=m;i++)
    {
	if(tree[i]) continue;
	p=ff(a[i]),q=ff(b[i]);
	for(;p!=q;)
	{
	    if(deep[p]<deep[q]) swap(p,q);
	    ans[p]=c[i];
	    bcj[p]=ff(fa[p]);
	    p=ff(p);
	}
    }
    for(i=1;i<=n;i++)
    {
	if(!need[i]) continue;
	if(!ans[i])
	{
	    puts("-1");
	    return 0;
	}
	sum+=ans[i];
    }
    cout<<sum;
}

猜你喜欢

转载自blog.csdn.net/yzyyylx/article/details/87941465