題目連結: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;
}