codeforces1023F Mobile Phone Network

題面

題意

有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;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章