最近好像一直懒得写题解,但是感觉还是写一写比较好。
首先若干个向量组成一个凸包有经典做法,就是把向量按照极角排序,然后按照极角顺序依次拼接,得到的就是一个凸包,且方案唯一(由于本题限制不存在共线的两个向量)。
那么我们实际上只需要知道每个向量最终用了多少就可以了。设第 \(i\) 个向量用了 \(c_i\) 个,那么这个向量集合能拼成凸包当且仅当它们的和为 \(0\),也就是 \(\sum_{x_i > 0} c_i x_i = \sum_{x_i < 0} c_i (-x_i), \sum_{y_i > 0} c_i y_i = \sum_{y_i < 0} c_i (-y_i)\)。
考虑高度的限制,由于凸包一定是先正后负,容易发现这就是在要求 \(\sum_{x_i > 0} c_i x_i \le m, \sum_{y_i > 0} c_i y_i \le m\)。
那么我们要求的就是满足:
\[\sum_{x_i > 0} c_i x_i = \sum_{x_i < 0} c_i (-x_i), \sum_{y_i > 0} c_i y_i = \sum_{y_i < 0} c_i (-y_i), \sum_{x_i > 0} c_i x_i \le m, \sum_{y_i > 0} c_i y_i \le m \]的 \(\{c_i\}\) 的数量。看起来不好做,但是注意到 \(x_i\) 很小,所以我们可以考虑数位 DP,按位确定 \(c_i\) 的值。这样这些限制很容易满足,直接做就行了。
#include <bits/stdc++.h>
using namespace std;
const int P = 998244353;
int n, m;
int x[5], y[5];
int g[18][18][18][18][2][2][30];
void add(int &a, int b) {
a += b;
if (a >= P) a -= P;
}
int f(int px, int nx, int py, int ny, bool xb, bool yb, int i) {
if (i == 30) return px == 0 && nx == 0 && py == 0 && ny == 0 && xb && yb;
int &cur = g[px][nx][py][ny][xb][yb][i];
if (cur != -1) return cur;
cur = 0;
for (int s = 0; s < (1 << n); s++) {
int npx = px, nnx = nx, npy = py, nny = ny;
for (int j = 0; j < n; j++) if (s >> j & 1) {
if (x[j] > 0) npx += x[j];
if (x[j] < 0) nnx -= x[j];
if (y[j] > 0) npy += y[j];
if (y[j] < 0) nny -= y[j];
}
if ((npx & 1) != (nnx & 1) || (npy & 1) != (nny & 1)) continue;
bool nxb = xb ? ((npx & 1) <= (m >> i & 1)) : ((npx & 1) < (m >> i & 1)),
nyb = yb ? ((npy & 1) <= (m >> i & 1)) : ((npy & 1) < (m >> i & 1));
add(cur, f(npx >> 1, nnx >> 1, npy >> 1, nny >> 1, nxb, nyb, i + 1));
}
return cur;
}
int main() {
scanf("%d%d", &n, &m);
for (int i = 0; i < n; i++) {
scanf("%d%d", &x[i], &y[i]);
}
memset(g, -1, sizeof g);
printf("%d\n", (f(0, 0, 0, 0, 1, 1, 0) - 1 + P) % P);
return 0;
}
标签:cur,int,sum,Shapes,CF1290F,npy,npx,&&,Making
From: https://www.cnblogs.com/apjifengc/p/17466328.html