T1 P4460 [CQOI2018]解锁屏幕
思路:
先看数据范围,想到状压
发现最后一个到达的点对 dp 有影响,于是记录一下
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j] 表示状态为 i,最后一个点为 j 的状态数
如果 j 到 k 点间的点全部走过,有转移方程
d
p
[
i
∣
(
1
<
<
k
)
]
[
k
]
+
=
d
p
[
i
]
[
j
]
dp[i|(1<<k)][k]+=dp[i][j]
dp[i∣(1<<k)][k]+=dp[i][j]
i 较大的状态只能由较小的状态转移来,可以循环模拟 DFS
发现暴力判断会 TLE,于是考虑预处理
时间复杂度
O
(
2
n
n
2
)
O(2^nn^2)
O(2nn2)
代码:
#include <bits/stdc++.h>
using namespace std;
#define in Read()
inline char ch() {
static char buf[1 << 21], *p1 = buf, *p2 = buf;
return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2)
? EOF
: *p1++;
}
inline int in {
int s = 0, f = 1;
char x;
for (x = ch(); x < '0' || x > '9'; x = ch())
if (x == '-') f = -1;
for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
return f == 1 ? s : -s;
}
const int A = 25;
const int mod = 1e8 + 7;
int n;
struct Point {
int x, y;
Point(int u = 0, int v = 0) { x = u, y = v; }
inline Point friend operator-(const Point u, const Point v) {
return Point(v.x - u.x, v.y - u.y);
}
inline int friend operator^(const Point u, const Point v) {
return u.x * v.y - u.y * v.x;
}
} p[A];
int d[A][A];
int f[1 << 21][A];
int res = 0;
inline int check(Point u, Point v, Point w) {
if (((v - u) ^ (w - u)) != 0) return 0;
if (u.x > v.x) swap(u, v);
if (w.x < u.x || w.x > v.x) return 0;
if (u.y > v.y) swap(u, v);
if (w.y < u.y || w.y > v.y) return 0;
return 1;
}
inline int find(int now) {
int num = 0;
for (int i = 0; i < n; i++)
if (now & (1 << i)) num++;
return num;
}
signed main() {
n = in;
for (int i = 0; i < n; i++) p[i].x = in, p[i].y = in;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
for (int k = j + 1; k < n; k++) {
if (check(p[i], p[j], p[k])) d[i][j] |= (1 << k), d[j][i] |= (1 << k);
if (check(p[i], p[k], p[j])) d[i][k] |= (1 << j), d[k][i] |= (1 << j);
if (check(p[j], p[k], p[i])) d[j][k] |= (1 << i), d[k][j] |= (1 << i);
}
}
}
for (int i = 0; i < n; i++) f[1 << i][i] = 1;
for (int i = 1; i < (1 << n); i++) {
for (int j = 0; j < n; j++) {
if ((i & (1 << j)) == 0) continue;
for (int k = 0; k < n; k++) {
if (i & (1 << k)) continue;
if ((i & d[j][k]) != d[j][k]) continue;
f[i | (1 << k)][k] = (f[i | (1 << k)][k] + f[i][j]) % mod;
}
}
}
for (int i = 1; i < (1 << n); i++) {
if (find(i) < 4) continue;
for (int j = 0; j < n; j++) {
if ((i & (1 << j)) == 0) continue;
res = (res + f[i][j]) % mod;
}
}
printf("%d\n", res);
return 0;
}