CodeForces 1070 M. Algoland and Berland
阿新 • • 發佈:2018-12-24
連結:
題意:
平面上有 個紅色點和 個藍色點,沒有三點共線,紅藍點之間可以連邊,求一棵生成樹,使得第 個藍點的度數恰好為 ,並且生成樹的連邊在平面上不會在除了端點的地方相交。
保證 。
題解:
考慮用歸納法給出構造。當 時,構造顯然。
否則,取出度數最大的點,用一條經過這個點的直線把平面劃分成兩部分,剩下的兩部分遞迴做。
考慮如何去劃分,記一個點的權值為 (我們認為紅點的 是 ),那麼其他點的權值和等於 。我們需要把其他點分成兩部分,使得每部分的權值和都小於等於 ,這樣就能保證連通性。
對於一定有解的證明:記一個半平面的權值和為 ,那麼限制相當於 。隨便畫一條線,如果 滿足要求了,就找到了一組解;否則將這條線轉一圈, 會變成 。在變化的過程中, 會從 變成 (或者反過來),而變化量不會超過 ,所以一定存在一個時刻 落在區間內部。
程式碼:
#include <bits/stdc++.h>
using namespace std;
typedef long double ld;
const ld pi = acos(-1);
const ld eps = 1e-9;
struct point_t {
int x, y, id, degree;
point_t(int x = 0, int y = 0, int id = 0, int degree = 0):x(x), y(y), id(id), degree(degree) {
}
};
void solve(vector<point_t> points) {
int max_degree = 0, max_id = -1;
for (int i = 0; i < points.size(); ++i) {
if (max_degree < points[i].degree) {
max_degree = points[i].degree;
max_id = i;
}
}
if (max_degree == 1) {
for (int i = 0; i < points.size(); ++i) {
if (!points[i].degree) {
max_id = i;
}
}
for (int i = 0; i < points.size(); ++i) {
if (i != max_id) {
cout << points[i].id + 1 << " " << points[max_id].id + 1 << endl;
}
}
} else {
int n = points.size() - 1;
swap(points[max_id], points[n]);
vector<pair<ld, int>> events;
for (int i = 0; i < n; ++i) {
ld from = atan2(points[i].y - points[n].y, points[i].x - points[n].x);
ld to = from + pi;
if (to > pi) {
to -= pi * 2;
}
events.emplace_back(from, i);
events.emplace_back(to, i + n);
}
sort(events.begin(), events.end());
vector<bool> side(n);
int sum = 0;
for (auto p : events) {
if (p.first > eps && p.second < n) {
sum += points[p.second].degree - 1;
side[p.second] = true;
}
}
for (auto p : events) {
if (sum <= -1 && sum >= 1 - max_degree) {
break;
}
if (p.second < n) {
sum += points[p.second].degree - 1;
side[p.second] = true;
} else {
sum -= points[p.second - n].degree - 1;
side[p.second - n] = false;
}
}
vector<point_t> left, right;
for (int i = 0; i < n; ++i) {
if (side[i]) {
left.push_back(points[i]);
} else {
right.push_back(points[i]);
}
}
left.emplace_back(points[n].x, points[n].y, points[n].id, -sum);
right.emplace_back(points[n].x, points[n].y, points[n].id, sum + max_degree);
solve(left);
solve(right);
}
}
int main() {
#ifdef wxh010910
freopen("input.txt", "r", stdin);
#endif
int tt;
cin >> tt;
while (tt--) {
int n, m;
cin >> n >> m;
vector<int> degree(m);
for (int i = 0; i < m; ++i) {
cin >> degree[i];
}
vector<point_t> points(n + m);
for (int i = 0; i < n; ++i) {
cin >> points[i].x >> points[i].y;
points[i].id = i;
}
for (int i = 0; i < m; ++i) {
cin >> points[i + n].x >> points[i + n].y;
points[i + n].degree = degree[i];
points[i + n].id = i;
}
cout << "YES" << endl;
solve(points);
}
return 0;
}