0%

2026 杭电暑期多校 5——括号凸包(判断一个凸包是否合法,由于起点和终点相同,因此我们肯定绕了个圈子,我们要判断的是到底绕了几圈?只需要看这个向量的方位角是否可通过循环位移排成递增即可啊)(方位角递增这个性质,可以通过这个枚举顺序实现,就不要通过增加 dp 状态实现)

题目大意

括号凸包

给定二维平面上的 nn 个点,所有点互不相同,且不存在三点共线。每个点上写有一个括号,可能是左括号 (\texttt{(} ,也可能是右括号 )\texttt{)}

我们称一个字符串 SS 是一个合法括号序列,当且仅当它可以由如下递归规则生成:

  • 空串是一个合法括号序列;

  • 如果 AA 是合法括号序列,那么字符串 (A)\texttt{(}A\texttt{)} 也是合法括号序列;

  • 如果 AABB 都是合法括号序列,那么 ABAB 也是合法括号序列。

例如,()\texttt{()}(())\texttt{(())}()()\texttt{()()} 都是合法括号序列,而 )(\texttt{)(}(()\texttt{(()}())(\texttt{())(} 不是合法括号序列。

现在,你需要从给定的点中选出若干个互不相同的点作为顶点,构成一个凸多边形。在本题中,一个由 mm 个点 pa1,pa2,,pamp_{a_1},p_{a_2},\dots,p_{a_m} 构成的多边形被称为凸多边形,当且仅当满足:

  • m3m \ge 3

  • 这些点按照 a1,a2,,ama_1,a_2,\dots,a_m 的顺序依次连接,并连接 ama_ma1a_1 后,形成一个简单多边形(即多边形的边仅在相邻边的端点处相交,不相邻边互不相交);

  • 对于该多边形的每一条边,其余所有顶点都严格位于这条边所在直线的同一侧。

换句话说,所选出的点必须恰好按照它们在自身凸包上的环形顺序排列,并且所有内角都严格小于 180180^\circ,凸多边形上不存在三点共线。

对于一个凸多边形,任选其边界上的一个顶点作为起点,并沿着多边形边界按顺时针或逆时针方向依次遍历所有顶点,最后回到起点前停止。这样可以得到一个长度为 mm 的括号序列:若当前顶点上写有左括号,则写下 (\texttt{(};若当前顶点上写有右括号,则写下 )\texttt{)}

你的任务是找到包含至少一个左括号的凸多边形,使得对于该多边形上的任意一个写有左括号的顶点,以它作为起点沿多边形边界并以任意方向遍历得到的括号序列都是合法括号序列。请计算满足条件的凸多边形个数,并将结果对 998244353998244353 取模后输出。

第一行包含一个整数 TT1T1001 \le T \le 100),表示数据组数。

对于每组数据:

第一行包含一个整数 nn1n5001 \le n \le 500),表示点的数量。

接下来 nn 行,每行包含三个整数 xi,yi,tix_i, y_i, t_i0xi,yi1090 \le x_i, y_i \le 10^9ti{0,1}t_i \in \{0, 1\}),表示第 ii 个点的坐标和括号类型。其中 ti=0t_i = 0 表示左括号, ti=1t_i = 1 表示右括号。

保证每组数据中所有点互不相同,且不存在三点共线

保证所有数据的 nn 之和不超过 10001000

对于每组数据,输出一行一个整数,表示满足条件的凸多边形方案数对 998244353998244353 取模后的结果。

Plaintext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
5
4
1 1 0
2 4 1
3 9 0
4 16 1
5
1 1 0
2 4 0
3 9 0
4 16 1
5 25 1
6
47 58 0
30 23 0
27 34 1
35 7 1
10 30 1
1 25 1
8
10 5 0
10 16 0
1 5 0
24 9 0
6 2 0
6 12 0
7 18 1
3 13 1
1
0 0 0

Plaintext

1
2
3
4
5
1
0
1
0
0
  • 对于第一组样例44 个点的坐标形如抛物线 y=x2y=x^2,因此这 44 个点的任意大小大于等于 33 的子集都能构成凸多边形。这四个点的括号类型依次为 (\texttt{(})\texttt{)}(\texttt{(})\texttt{)}。若选择全部 44 个点构成凸四边形,其顺时针与逆时针的环形括号序列循环节均为 ()()\texttt{()()}。无论从第 11 个点(左括号)还是第 33 个点(左括号)出发,按顺时针或逆时针方向生成的字符串均为 ()()\texttt{()()},是合法序列。可以证明,选取其他任何 33 个点构成的三角形均因括号数量为奇数而无法满足条件。因此,方案数为 11

  • 对于第二组样例:给出 55 个点,括号类型依次为 (\texttt{(}(\texttt{(}(\texttt{(})\texttt{)})\texttt{)}。经尝试,无法选出任何一个使得“从任意左括号出发、沿任意方向遍历”均产生合法序列的凸多边形。方案数为 00

  • 对于第五组样例:只有 11 个点,无法满足构成凸多边形要求的最少顶点数(m3m \ge 3),故方案数为 00

思路讲解

image

首先,题目中有关于括号凸包的表述啊,这个括号串肯定是形如这个 ()()()()...()()()()()()...()() 这样子的形式啊。

因为每个括号串的第一个字符肯定是这个 ,最后一个字符肯定是 ,所有奇数位置必是 ,这个是因为所有的奇数位置肯定是这个开头(题目中要求每个左括号位置循环移动到开头仍然是合法括号串)。

然后接着就是标题里面的结论啊:

判断一个凸包是否合法,只需要看这个向量的方位角是否可通过循环位移排成递增即可啊,当然前提是不能 3 点共线

image

那么这个结论实际上是这样子来的啊:

image

image

image

因为我们最终会绕一圈(起点和终点相同),因此我们其实是在判断,是否多绕了一圈

这个三点不能共线的规定是用在这个 dp 转移里面的。

dp 状态我们先这样子定义,但是这样子:

image

image

image

1
2
3
4
vector<vector<ll> > dp(N, vector<ll>(N));
for (int i = 0; i < N; ++i) {
dp[i][i] = 1;
}

这样子会多计算出来一些东西,最后减掉就行啊。

1
2
3
4
5
6
7
for (int i = 0; i < N; ++i) {
ans += dp[i][i];
ans %= mod;
}
// 减掉多余的东西啊
ans -= SZ(vec_ijs) / 2;
ans -= N;

具体转移的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
vector<VecIJ> vec_ijs;
vec_ijs.reserve(N * N);
for (int i = 0; i < SZ(A); ++i) {
for (int j = 0; j < SZ(A); ++j) {
if (i == j) continue;
if (A[i].c != A[j].c) {
auto diff = A[j].p - A[i].p;
vec_ijs.push_back({diff, i, j});
}
}
}
// 进行极角排序(上面重载过 VecIJ 的比较运算符号)
sort(all(vec_ijs));
// 按照边的方位角方向,进行这个转移
for (auto [_,u,v]: vec_ijs) {
for (int src = 0; src < N; ++src) {
dp[src][v] += dp[src][u];
dp[src][v] %= mod;
}
}

AC代码

AC
https://acm.hdu.edu.cn/contest/view-code?cid=1233&rid=11368

心路历程(WA,TLE,MLE……)