题目链接:http://codeforces.com/contest/1141/problem/G
题意:现有一棵树,n个节点,n-1条边,你可以选择[1, r]中选数标在边上,和一个节点相连的边不能有两个标号一样的,但是可以选择k个节点打破这个规则,问r最小是多少。
解题心得:
- 就是一个贪心,既然可以选择k个节点打破规则,那肯定按照结点的度排序,那么r就是降序节点中的第k-1个节点的度。然后将前k个节点标记一下。
- 跑一个树上bfs,如果遇到标记的节点,将所有和他相连的边都标记成一个数,不然就循环标记。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 100;
//规定在记录边的时候pair<int,int> 第一个节点的标号比第二个小
vector<int> ve[maxn];//邻接表
vector<pair<int, int> > edge, degree;//记录输入顺序的边和每个节点的度
map<pair<int, int>, int> ans;//记录最后每个边标记的答案是多少
bool vis[maxn];//标记可以打破规则的节点
int n, k, R;
struct Last_edge {//记录在BFS中当前节点的父节点,当前节点与父节点边的标号,当前节点的父节点
int u, num, pre;
Last_edge() {};
Last_edge(int u, int num, int pre) : u(u), num(num), pre(pre) {};
};
void init() {
scanf("%d%d", &n, &k);
for (int i = 1; i < n; i++) {
int a, b;
scanf("%d%d", &a, &b);
if (a > b) swap(a, b);
edge.push_back(make_pair(a, b));
ans[make_pair(a, b)] = -1;
ve[a].push_back(b);
ve[b].push_back(a);
}
}
void checke_max_degree() {//按照节点度降序排列,找出R,标记可以打破规则的节点
for (int i = 1; i <= n; i++) {
degree.push_back(make_pair(ve[i].size(), i));
}
sort(degree.begin(), degree.end());
reverse(degree.begin(), degree.end());
for (int i = 0; i < k; i++) {
int u = degree[i].second;
vis[u] = true;
}
R = degree[k].first;
printf("%d\n", R);
}
bool BFS() {
queue<Last_edge> qu;
qu.push(Last_edge(1, 0, -1));
while (!qu.empty()) {
Last_edge now = qu.front();
qu.pop();
int num = (now.num + 1) % (R + 1);
if (num == 0) num = 1;
for (int i = 0; i < ve[now.u].size(); i++) {
int v = ve[now.u][i];
if(v == now.pre) continue;
int a = now.u, b = v;
if (a > b) swap(a, b);
if (vis[now.u]) {
qu.push(Last_edge(v, 1, now.u));
ans[make_pair(a, b)] = 1;
continue;
}
qu.push(Last_edge(v, num, now.u));
ans[make_pair(a, b)] = num;
num = (num + 1) % (R + 1);
if (num == 0) num = 1;
}
}
}
int main() {
// freopen("1.in", "r", stdin);
init();
checke_max_degree();
BFS();
for (int i = 0; i < edge.size(); i++) {
int a = edge[i].first;
int b = edge[i].second;
if (a > b) swap(a, b);
printf("%d ", ans[make_pair(a, b)]);
}
return 0;
}