1. 程式人生 > >CodeForces 1070 M. Algoland and Berland

CodeForces 1070 M. Algoland and Berland

連結:

link

題意:

平面上有 aa 個紅色點和 bb 個藍色點,沒有三點共線,紅藍點之間可以連邊,求一棵生成樹,使得第 ii 個藍點的度數恰好為 rir_i ,並且生成樹的連邊在平面上不會在除了端點的地方相交。

保證 ri=a+b1,1ria\sum r_i = a+b-1, 1\le r_i\le a

題解:

考慮用歸納法給出構造。當 maxri=1\max r_i = 1 時,構造顯然。

否則,取出度數最大的點,用一條經過這個點的直線把平面劃分成兩部分,剩下的兩部分遞迴做。

考慮如何去劃分,記一個點的權值為 r

i1r_i - 1 (我們認為紅點的 rir_i00 ),那麼其他點的權值和等於 maxri-\max r_i 。我們需要把其他點分成兩部分,使得每部分的權值和都小於等於 1-1 ,這樣就能保證連通性。

對於一定有解的證明:記一個半平面的權值和為 sumsum ,那麼限制相當於 1maxrisum11 - \max r_i\le sum\le -1 。隨便畫一條線,如果 sumsum 滿足要求了,就找到了一組解;否則將這條線轉一圈, sumsum 會變成 summaxri-sum - \max r_i

。在變化的過程中,sumsum 會從 <1maxri< 1 - \max r_i 變成 >1> -1 (或者反過來),而變化量不會超過 maxri1\max r_i - 1 ,所以一定存在一個時刻 sumsum 落在區間內部。

程式碼:

#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; }