题目链接
题意
给定
N
个点,对于一个凸
对所有的 score 求和,输出 mod 998244353 的值。
思路
考虑一个凸
n
边形,其内与其上共
取
0
个,取
所以可以说,对于任意 m 个点,只要其中有一个子集能构成一个凸多边形,那么其对最后答案就有贡献为。
贡献为多少呢?我们猜想是
于是就得到了一个双射,问题就转化成了,有多少个点集满足其子集能构成凸多边形,也即有多少个点集不是所有点共线。
这里有两种做法。
法一:暴力
复杂度: O(n3)
枚举两个顶点,看有多少个点在这两个顶点连成的线上。枚举的两个点固定,其他所有点中至少取一个,情况数就是这条线上共线的点集个数。显然,不会有重复。累和,即为最终答案。
Code
#include <bits/stdc++.h>
#define maxn 100010
typedef long long LL;
const LL mod = 998244353;
LL x[maxn], y[maxn], P[maxn];
int main() {
LL n;
scanf("%lld", &n);
for (int i = 0; i < n; ++i) scanf("%lld%lld", &x[i], &y[i]);
P[0] = 1;
for (int i = 1; i <= n; ++i) (P[i] = P[i-1] << 1) %= mod;
LL ans = P[n] - 1 - n - n*(n-1)/2;
for (int i = 0; i < n; ++i) {
for (int j = i+1; j < n; ++j) {
int cnt = 0;
for (int k = j+1; k < n; ++k) {
if ((x[j]-x[i]) * (y[k]-y[j]) == (x[k]-x[j]) * (y[j]-y[i])) ++cnt;
}
((ans -= P[cnt]-1) += mod) %= mod;
}
}
printf("%lld\n", ans);
return 0;
}
法二:并查集
复杂度: O(n2logn)
参考:[AtCoder]AtCoder Regular Contest 082 CDEF ——jstztzy
十分巧妙的方法,先预处理出两两点对之间的线段,再将线段排个序,那么所有共线的线段肯定都排在了一起并且具有相同的属性(原博是用的 hash ,我就直接比较了比例)。
那么对于这一些线段,对其中的任一条线段,将其两个端点 union 一下,如果是共线的线段,最后所有的顶点肯定都会在同一个 set 里面。
Code
#include <bits/stdc++.h>
#define maxn 100010
typedef long long LL;
using namespace std;
const LL mod = 998244353;
LL x[maxn], y[maxn], P[maxn];
int fa[maxn], sz[maxn];
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
void unionn(int x, int y) {
x = find(x), y = find(y);
if (x == y) return;
if (sz[x] > sz[y]) swap(x, y);
fa[x] = y; sz[y] += sz[x];
}
struct node { int p, q; LL dx, dy; };
bool cmp(node u, node v) { return u.dx < v.dx || (u.dx == v.dx && u.dy < v.dy); }
vector<node> seg;
LL GCD(LL x, LL y) { return y ? GCD(y, x % y) : x; }
int main() {
LL n;
scanf("%lld", &n);
for (int i = 0; i < n; ++i) scanf("%lld%lld", &x[i], &y[i]);
P[0] = 1;
for (int i = 1; i <= n; ++i) (P[i] = P[i-1] << 1) %= mod;
for (int i = 0; i < n; ++i) {
for (int j = i+1; j < n; ++j) {
LL dx = x[i]-x[j], dy = y[i]-y[j];
LL gcd = GCD(dx, dy);
dx /= gcd, dy /= gcd;
if (dx < 0) dx = -dx, dy = -dy;
seg.push_back(node{i, j, dx, dy});
}
}
sort(seg.begin(), seg.end(), cmp);
LL ans = P[n] - 1 - n;
int i = 0, j = 0;
for (; i < seg.size(); i = j+1) {
for (int k = 0; k < n; ++k) fa[k] = k, sz[k] = 1;
for (j = i; ; ++j) {
unionn(seg[j].p, seg[j].q);
if (j+1 >= seg.size() || seg[i].dx != seg[j+1].dx || seg[i].dy != seg[j+1].dy) break;
}
for (int k = 0; k < n; ++k) {
if (fa[k] == k) ((ans -= (P[sz[k]] - sz[k] - 1)) += mod) %= mod;
}
}
printf("%lld\n", ans);
return 0;
}
// 可能是我写ci了,暴力的跑了 3ms ,并查集跑了 26ms …