[BZOJ5299] [CQOI2018]解锁屏幕

Description

使用过Android手机的同学一定对手势解锁屏幕不陌生。Android的解锁屏幕由3x3个点组成,手指在屏幕上画一条
线将其中一些点连接起来,即可构成一个解锁图案。如下面三个例子所示:
画线时还需要遵循一些规则
1.连接的点数不能少于4个。也就是说只连接两个点或者三个点会提示错误。
2.两个点之间的连线不能弯曲。
3.每个点只能"使用"一次,不可重复。这里的"使用"是指手指划过一个点,该点变绿。
4.两个点之间的连线不能"跨过"另一个点,除非那个点之前已经被"使用"过了。
对于最后一条规则,参见下图的解释。左边两幅图违反了该规则:而右边两幅图(分别为2→4→1→3→6和→5→4→1→9→2)
则没有违反规则,因为在"跨过"点时,点已经被"使用"过了。
现在工程师希望改进解锁屏幕,增减点的数目,并移动点的位置,不再是一个九宫格形状,但保持上述画线的规则不变。
请计算新的解锁屏幕上,一共有多少满足规则的画线方案。

Input

输入文件第一行,为一个整数n,表示点的数目。
接下来n行,每行两个空格分开的整数xi和yi,表示每个点的坐标。
-1000≤xi,Yi≤l000,1≤n<20。各点坐标不相同

Output

输出文件共一行,为题目所求方案数除以100000007的余数。

Sample Input

4
0 0
1 1
2 2
3 3

Sample Output

8
解释:设4个点编号为1到4,方案有1→2→3→4,2→1→3→4,3→2→1→4,2→3→1→4,
及其镜像4→3→2→1,3→4→2→1,2→3→4→1,3→2→4→1.

 


 

 

开始把n的范围看成10了,Re了好久才发现...

然后又卡精度Wa了好久才发现...

 

就是设f[i][S]表示已选集合状态为S, 最后一个选择的点是i的方案总数。

先预处理出来sit[i][j]表示要连接i和j需要提前连起来的点的集合。

然后就随便转移了...

最后统计答案的时候要注意一下。

这题没有别人说的那么卡常(我自带小常数), 就是卡精度。

总体来说比较水,甚至放在Noip都比较水

 


 

 

 

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <bitset>
using namespace std;
#define reg register
#define mod 100000007 

inline int read() {
    int res=0;char ch=getchar();bool flag=0;
    while(!isdigit(ch)){if(ch=='-')flag=1;ch=getchar();}
    while(isdigit(ch))res=(res<<3)+(res<<1)+(ch^48),ch=getchar();
    return flag?-res:res;
}

int n, x[22], y[22];
int sit[22][22], bin[22];
int f[22][1<<22];
int ans;

signed main()
{
    bin[0] = 1;for (int i = 1 ; i <= 21 ; i ++) bin[i] = bin[i-1] << 1;
    n = read();
    for (reg int i = 1 ; i <= n ; i ++) x[i] = read(), y[i] = read();
    for (reg int i = 1 ; i <= n ; i ++)
    {
        for (reg int j = 1 ; j <= n ; j ++)
        {
            if (i == j) continue;
            double k = 1.0, b = 1.0;
            if (x[i] != x[j]) k = (double)(((double)y[i] - (double)y[j]) / (double)((double)x[i] - (double)x[j]));
            b = (double)y[i] - k * (double)x[i];
            for (reg int p = 1 ; p <= n ; p ++)
            {
                if (i == p or j == p) continue;
                if (x[i] == x[j] and x[i] == x[p] and y[p] <= max(y[i], y[j]) and y[p] >= min(y[i], y[j]))  sit[i][j] |= bin[p-1];
                else {
                    if (x[i] != x[j])
                        if (fabs((double)y[p] - (double)x[p] * k - b) <= 1e-13 and x[p] <= max(x[i], x[j]) and x[p] >= min(x[i], x[j]))  sit[i][j] |= bin[p-1];
                }
            }
        }
    }
    for (reg int i = 1 ; i <= n ; i ++) f[i][bin[i-1]] = 1;
    for (reg int S = 0 ; S <= (1 << (n)) - 1 ; S ++)
    {
        for (reg int i = 1 ; i <= n ; i ++)
        {
            if ((S & bin[i-1]) == 0) continue;
            for (reg int j = 1 ; j <= n ; j ++)
            {
                if (i == j) continue;
                if (S & bin[j-1]) continue;
                if ((S & sit[i][j]) != sit[i][j]) continue;
                f[j][S|bin[j-1]] = (f[j][S|bin[j-1]] + f[i][S]) % mod;
            }
        }
    }
    for (reg int S = 0 ; S <= (1 << (n)) - 1 ; S ++)
    {
        bitset <22> bit = S;
        if (bit.count() <= 3) continue;
        for (reg int i = 1 ; i <= n ; i ++) ans = (ans + f[i][S]) % mod;
    }
    printf("%d\n", ans);
    return 0;
}

 

posted @ 2018-08-21 18:02  zZhBr  阅读(206)  评论(0编辑  收藏  举报