C. Maximize the Intersections
[link](Problem - C - Codeforces)
题意
在一个圆上有2n个不同的点,具有以下性质:无论你如何选择3条连接3个互不相干的点的弦,在圆内没有一个点严格属于所有3条弦。这些点按顺时针顺序被编号为1,2,…,2n。
最初,k条和弦连接了k对点,其方式是这些和弦的所有2k个端点是不同的。
你想画出n-k条额外的和弦,连接剩余的2(n-k)个点(每个点必须正好是一个和弦的端点)。
最后,让x成为所有n条弦的交叉点的总数。计算如果你以最佳方式选择n-k条弦,x能达到的最大值。
请注意,2n个点的确切位置并不重要,只要第一段中所述的属性成立即可。
题解
首先对于两条弦是否相交如何判断?
假设x1 < y1, x2 < y2且x1 < x2则当且仅当 x2 < y1 && y1 < y2时两弦有交点
什么情况下交点数最多?
假设有2n个点,从 1 - n + 1连一条边,这个弦的两侧的点数相同,弦的左右的点两两配对,则其余点对于该弦的贡献就是n - 1,因此,我们将所有的点按照 i - i + n 这种形式排列好,那么每一条弦的贡献一定都是n - 1,因为一共2n个点,n个弦,每个弦最多贡献是n - 1所以这种情况一定是最优的,因此,把剩下的没连过的点按照相对顺序依照这种方法连接即可。然后暴力跑一遍一否成立即可。
Code
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <set>
#include <queue>
#include <vector>
#include <map>
#include <unordered_map>
#include <cmath>
#include <stack>
#include <iomanip>
#define x first
#define y second
#define eps 1e-7
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef unsigned long long ULL;
const int N = 5e4 + 10, M = 30010, INF = 0x3f3f3f3f, P = 131, mod = 1e9 + 7;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int n, m;
bool check(PII c, PII d) { // 判断是否有交点
if (c.x > d.x) swap(c, d); // c靠前
return c.y < d.y && d.x < c.y;
}
int main () {
cin.tie(nullptr)->sync_with_stdio(false);
int T;
cin >> T;
while (T --) {
cin >> n >> m; // n条弦和用了m条
vector<PII> chords; // 存所有弦的xi,yi
vector<bool> used(2 * n + 1, false); // 判断哪些弦题目中给了
for (int i = 1; i <= m; i ++ ) {
int x, y;
cin >> x >> y;
if (x > y) swap(x, y); // 从小到大 定序 方便后面操作
chords.push_back({x, y}); // 将弦加入
used[x] = used[y] = true; // 表示用过了
}
vector<int> unused; // 存没用过的弦
for (int i = 1; i <= 2 * n; i ++ )
if (!used[i]) unused.push_back(i); // 找到所有没用过的弦
for (int i = 0; i < n - m; i ++ ) // 将所有没用过的弦按照相对顺序,依照最优策略连弦
chords.push_back({unused[i], unused[i + n - m]}); // 加入到方案内
int res = 0;
for (int i = 0; i < n; i ++ ) // 暴力枚举
for (int j = i + 1; j < n; j ++ )
res += check(chords[i], chords[j]); // 是否合理
cout << res << endl;
}
return 0;
}