解锁屏幕
题目背景:
分析:状压DP
比较明显是状压,一开始以为是子集枚举,然后看数据范围觉得自己凉了,所以就先跳过了,最后导致没有做这道题,然后凉凉凉,考完发现是一道简单题·····定义f[i][stats]表示已经当前已经用了状态stats里的点,那么直接枚举下一个点走哪里即可,可以先n3预处理出两两点对之间经过哪些点,这样就可以在转移中O(1)判定转移是否合法了,因为复杂度有一些卡,在枚举转移的时候不能直接for每一位,可以直接每一次提取lowbit,这样的复杂度是O(n2 * 2n)常数比较小,可以在时限内通过。
Source:
/*
created by scarlyw
*/
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cctype>
#include <vector>
#include <set>
#include <queue>
#include <ctime>
#include <bitset>
inline char read() {
static const int IN_LEN = 1024 * 1024;
static char buf[IN_LEN], *s, *t;
if (s == t) {
t = (s = buf) + fread(buf, 1, IN_LEN, stdin);
if (s == t) return -1;
}
return *s++;
}
///*
template<class T>
inline void R(T &x) {
static char c;
static bool iosig;
for (c = read(), iosig = false; !isdigit(c); c = read()) {
if (c == -1) return ;
if (c == '-') iosig = true;
}
for (x = 0; isdigit(c); c = read())
x = ((x << 2) + x << 1) + (c ^ '0');
if (iosig) x = -x;
}
//*/
const int OUT_LEN = 1024 * 1024;
char obuf[OUT_LEN];
char *oh = obuf;
inline void write_char(char c) {
if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf;
*oh++ = c;
}
template<class T>
inline void W(T x) {
static int buf[30], cnt;
if (x == 0) write_char('0');
else {
if (x < 0) write_char('-'), x = -x;
for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
while (cnt) write_char(buf[cnt--]);
}
}
inline void flush() {
for (int i = 0; i <= 10; ++i) std::cout << obuf[i];
std::cout << '\n';
fwrite(obuf, 1, oh - obuf, stdout), oh = obuf;
}
/*
template<class T>
inline void R(T &x) {
static char c;
static bool iosig;
for (c = getchar(), iosig = false; !isdigit(c); c = getchar())
if (c == '-') iosig = true;
for (x = 0; isdigit(c); c = getchar())
x = ((x << 2) + x << 1) + (c ^ '0');
if (iosig) x = -x;
}
//*/
const int mod = 100000000 + 7;
const int MAXN = 20;
long long f[MAXN][1 << MAXN | 1];
struct point {
int x, y;
point() {}
point(int x, int y) : x(x), y(y) {}
inline point operator + (const point &a) const {
return point(x + a.x, y + a.y);
}
inline point operator - (const point &a) const {
return point(x - a.x, y - a.y);
}
inline int operator * (const point &a) const {
return x * a.x + y * a.y;
}
inline int operator ^ (const point &a) const {
return x * a.y - y * a.x;
}
} p[MAXN];
int n;
int id[1 << MAXN | 1], cnt[1 << MAXN | 1];
int able[MAXN][MAXN];
inline void read_in() {
R(n);
for (int i = 0; i < n; ++i) R(p[i].x), R(p[i].y);
for (int i = 0; i < n; ++i)
for (int j = i + 1; j < n; ++j) {
int stats = 0;
for (int k = 0; k < n; ++k) {
if (k == i || k == j) continue ;
if ((p[i] - p[k] ^ p[j] - p[k]) == 0 &&
(p[i] - p[k]) * (p[j] - p[k]) < 0) {
stats |= (1 << k);
}
}
able[i][j] = able[j][i] = stats;
}
}
inline void solve() {
for (int i = 0; i < n; ++i) f[i][1 << i] = 1, id[1 << i] = i;
int tot = (1 << n) - 1;
for (int i = 1; i <= tot; ++i) cnt[i] = cnt[i >> 1] + (i & 1);
for (int i = 1; i < tot; ++i) {
int x = i, y = tot ^ i, u, v, z;
while (x != 0) {
u = (x & -x), z = y, x -= u, u = id[u];
while (y != 0) {
v = (y & -y), y -= v, v = id[v];
if ((able[u][v] & i) == able[u][v])
f[v][i | (1 << v)] += f[u][i];
}
y = z;
}
}
long long ans = 0;
for (int i = 0; i <= tot; ++i)
if (cnt[i] > 3)
for (int j = 0; j < n; ++j)
ans += f[j][i];
std::cout << ans % mod;
}
int main() {
//freopen("in.in", "r", stdin);
read_in();
solve();
return 0;
}