题目链接
http://www.bnuoj.com/v3/contest_show.php?cid=9358#problem/F
题目
Time Limit: 1000msMemory Limit: 262144KB 64-bit integer IO format: %lld Java class name: Main
Submit Status PID: 53076
ghc很喜欢吃汤圆,但是汤圆很容易被粘(zhān)漏。
根据多年吃汤圆经验,ghc总结出了一套汤圆防漏理论:
互相接触的汤圆容易粘(zhān)在一起,并且接触面积不同,粘(zhān)在一起的粘(nián)度也不同。
当ghc要夹起一个汤圆时,这个汤圆和现在碗里与这个汤圆接触的所有汤圆之间的粘(nián)度的和,如果大于汤圆的硬度,这个汤圆就会被粘(zhān)漏。
今天ghc又要煮汤圆啦,今天要煮n个汤圆,并且摆盘的方法已经设计好:
汤圆按照1, 2, \dots , n编号,有m对汤圆互相接触,用x_i, y_i, z_i表示编号为x_i和y_i的两个汤圆互相接触,粘(nián)度为z_i。
汤圆当然是越软越好吃,但是ghc的厨艺只允许把所有汤圆煮成同样的硬度。那么,汤圆的硬度最小可以是多少,可以满足吃的过程中,存在一种夹汤圆的顺序,使得没有汤圆会被粘(zhān)漏呢?
注意:
不考虑汤圆的重力作用;
不能同时夹多个汤圆;
吃完汤圆一定要喝点汤。
Input
第一行是一个正整数T(\leq 5),表示测试数据的组数,
对于每组测试数据,
第一行是两个整数n,m(1\leq n,m\leq 100000),
接下来m行,每行包含三个整数x_i, y_i, z_i(1\leq x_i, y_i \leq n, x_i \neq y_i, 1 \leq z_i \leq 1000000),
同一对汤圆不会出现两次。
Output
对于每组测试数据,输出一行,包含一个整数,表示汤圆硬度的最小值。
Sample Input
1
4 6
1 2 2
1 3 2
1 4 2
2 3 3
2 4 3
3 4 5
Sample Output
6
分析
用一个有序集合set来维护汤圆,set里面存放一个二元组<粘度和,汤圆编号>。每次取出set的第一个元素,也就是粘度和最小的汤圆,然后修改与该汤圆所有相接触的汤圆的粘度和。set本身是不支持修改集合内元素的功能的,但是可以通过删除原有元素、插入修改后元素来实现。只需遍历一遍汤圆和所有边,所有时间复杂度为O( (n+m)logn).
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+100;
typedef long long ll;
typedef pair<ll,ll> pa;
set<pa> s; //set自动排序,按第一个关键字
vector<pa> g[maxn];
ll nd[maxn]; //汤圆的粘度
ll vis[maxn];
void init()
{
memset(nd,0,sizeof(nd));
for(int i=0;i<maxn;i++) g[i].clear();
s.clear();
memset(vis,0,sizeof(vis));
}
int main()
{
ll T;
cin>>T;
while(T--)
{
init();
ll n,m;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int x,y,z;
cin>>x>>y>>z;
g[x].push_back(pa(y,z));
g[y].push_back(pa(x,z));
nd[x]+=z;
nd[y]+=z;
}
//将所有汤圆放入集合
for(int i=1;i<=n;i++) s.insert(make_pair(nd[i],i));
//一个一个取汤圆
ll ans=0;
while(!s.empty())
{
set<pa>::iterator it;
it=s.begin();
pa now=*it;
ans=max(ans,now.first);
s.erase(it);
//更新粘度
ll u=now.second;
vis[u]=1;
for(ll i=0;i<g[u].size();i++)
{
ll v=g[u][i].first;
if(vis[v]) continue;//只看父子边
it=s.find(pa(nd[v],v));
s.erase(it);
nd[v]-=g[u][i].second;
s.insert(pa(nd[v],v));
}
}
cout<<ans<<endl;
}
return 0;
}