#include <stdio.h>
#include <string>
#include <map>
#include <vector>
using namespace std;
map<string, string> father; //编号i的父节点为j
map<string, int> height; //记录编号i的高度
string FindDisjointSet(string u) {
if (father.find(u) != father.end()) { //father哈希表对应键u的值不为空
if (father[u] != u) { //非根结点 ,可以进行查找操作
father[u] = FindDisjointSet(father[u]); //寻找u的根并将父节点指向其
}
} else { //初始化,father哈希表对应键u的值为空
father[u] = u; //初始的父结点指向自己
height[u] = 0;
}
return father[u]; //返回集合的根结点
}
void UnionDisjointSet(string u, string v) {
string uroot = FindDisjointSet(u); //寻找u的根结点
string vroot = FindDisjointSet(v);//寻找v的根结点
if (uroot != vroot) { //不合并才可以操作
if (height[uroot] > height[vroot]) { //小数并入大树
father[vroot] = uroot;
} else if (height[uroot] < height[vroot]) { //小数并入大树
father[uroot] = uroot;
} else { //高度一致随便合并假设u为上,v为下 //初始时高度均一致
father[vroot] = uroot;
height[vroot]++; //并合并的集合树高+1
}
}
}
int main() {
int n, k;
int setCount; //记录团伙的数目
while (scanf("%d%d", &n,
&k) != EOF) { //电话的数量和权值(理解为边的权值)
map<string, int> SetMap; //存储每个名字对应的权值
vector<string> root; //记录每个集合(团伙)根结点
setCount = 0;
for (int i = 0; i < n; i++) {
char name1[20];
char name2[20];
int time;
scanf("%s%s%d", name1, name2, &time);
SetMap[name1] += time; //权值 = 时间累加
SetMap[name2] += time;
UnionDisjointSet(name1, name2); //以名字作为编号合并
}
auto it = father.begin();
for (it = father.begin(); it != father.end(); it++) {
//找出每个集合(团伙)的根结点便于分出有几个团伙!
if (it->first == it->second) {
root.push_back(it->first);
}
}
auto it1 = SetMap.begin();
//记录根结点以及对应集合的各个元素(人)
vector<vector<string>> Set(root.size(), vector<string>(SetMap.size()));
//记录每个集合(团伙)的总权值
vector<float> weight(root.size());
for (int i = 0; i < root.size();
i++) {//根结点以及对应集合的各个元素(人)
int j = 0;
for (it1 = SetMap.begin(); it1 != SetMap.end(); it1++) { //每个结点及时间
//从结点-时间哈希表头开始遍历,根结点一样则属于一个集合i(团伙)
if (FindDisjointSet(it1->first) == root[i]) {
Set[i][j] = it1->first; //将对应的键即名字放入同一个集合
//团伙总权值的计算
weight[i] += float(SetMap[Set[i][j]]) /
2; //算总时间的时候多加了一半要/2
j++;
}
}
}
for (int i = 0; i < root.size(); i++) { //外层循环
int l = 0; //记录每个集合(团伙)的个数
for (int j = 0; j < Set[i].size(); j++) { //内存循环
if (!Set[i][j].empty()) {
l++;
}
}
if (l > 2 && weight[i] > k) { //判断是否为团伙,1.成员>2 2.总权值>k
setCount++;
}
}
if (setCount > 0) { //先输出当前团伙的个数
printf("%d\n", setCount);
} else {//若当前团伙的个数为0 后序的寻找团伙头目无需再进行!直接结束本次循环
printf("0\n");
continue;
}
map<string, int>
Result; //按字母顺序记录最终的团伙头目及对应团伙成员数
for (int i = 0; i < root.size(); i++) {
int Maxcount = 0; //记录哪个人的权值最大?--》头目
string MaxString; //更新最大权值对应的人(名字)
int l = 0; //记录每个团伙的成员数
for (int j = 0; j < Set[i].size(); j++) {
if (!Set[i][j].empty()) {
l++;
}
}
if (l > 2 && weight[i] > k) { //满足团伙条件才将结果进行保存
for (int r = 0; r < Set[i].size(); r++) {
if (SetMap[Set[i][r]] > Maxcount) {
Maxcount = SetMap[Set[i][r]];
MaxString = Set[i][r];
}
}
//将结果插入一个有序哈希表 即按罪犯头目名字的字母从小到大顺序进行存储
Result.insert({MaxString.c_str(), l});
}
}
auto it2 = Result.begin();
//最后按序输出名字及对应的集合大小
for (it2 = Result.begin(); it2 != Result.end(); it2++) {
printf("%s %d\n", it2->first.c_str(), it2->second);
}
}
return 0;
}