首页 > 技术文章 > [CQOI 2018]解锁屏幕

NaVi-Awson 2018-05-04 11:22 原文

Description

题库链接

给出平面上 $n$ 个点,一开始你可以选任何一个点作为起点,接着对于每一个你在的位置,你可以选取一个未走过的点。将路径(线段)上所有的点均选上(包括起点终点),并走到选择的那个点上。询问选的点的个数 $\geq 4$ 的方案数。区别不同的方案,只需要路径不一样即可。

$1\leq n\leq 20$

Solution

状压 $dp$ 。

先预处理出两点间路径上会经过的点。 $dp$ 的时候需要选择路径上不会经过未选择点的方案走。

复杂度 $O(n^3+2^nn^2)$ 。

Code

#include <bits/stdc++.h>
#define lowbit(x) ((x)&(-(x)))
using namespace std;
const int N = 20+5, SIZE = (1<<20)+5, yzh = 100000007;

struct point {
    int x, y;
    point (int _x = 0, int _y = 0) {x = _x, y = _y; }
    point operator - (const point &b) const {return point(x-b.x, y-b.y); }
    int operator * (const point &b) const {return x*b.y-y*b.x; }
}a[N];
int n, mp[N][N], bin[N], f[SIZE][N], cnt[SIZE], ans;

void check(int x, int y) {
    int mxx = max(a[x].x, a[y].x), mnx = min(a[x].x, a[y].x);
    int mxy = max(a[x].y, a[y].y), mny = min(a[x].y, a[y].y);
    for (int i = 1; i <= n; i++)
        if (i != x && i != y)
            if ((a[y]-a[x])*(a[i]-a[x]) == 0)
                if (a[i].x >= mnx && a[i].x <= mxx && a[i].y >= mny && a[i].y <= mxy)
                mp[x][y] |= bin[i-1];
}
void work() {
    scanf("%d", &n);
    bin[0] = 1; for (int i = 1; i <= n; i++) bin[i] = bin[i-1]<<1;
    for (int i = 1; i <= bin[n]; i++) cnt[i] = cnt[i-lowbit(i)]+1;
    for (int i = 1; i <= n; i++) scanf("%d%d", &a[i].x, &a[i].y);
    for (int i = 1; i <= n; i++) for (int j = i+1; j <= n; j++) check(i, j);
    f[0][0] = 1;
    for (int i = 0; i < bin[n]; i++)
        for (int j = 0; j <= n; j++) if (f[i][j])
            for (int k = 1; k <= n; k++) if (!(bin[k-1]&i)) {
                int x = j, y = k; if (x > y) swap(x, y);
                if ((i&mp[x][y]) == mp[x][y]) (f[i|bin[k-1]][k] += f[i][j]) %= yzh;
            }
    for (int i = 0; i < bin[n]; i++) if (cnt[i] >= 4)
        for (int j = 1; j <= n; j++) (ans += f[i][j]) %= yzh;
    printf("%d\n", ans);
}
int main() {work(); return 0; }

推荐阅读