zoukankan      html  css  js  c++  java
  • [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
    ", ans);
        return 0;
    }
  • 相关阅读:
    TeX中的引号
    竖式问题
    蛇形填数
    开灯问题
    排列
    分数化小数
    子序列的和
    cookie
    post请求
    get请求
  • 原文地址:https://www.cnblogs.com/BriMon/p/9513009.html
Copyright © 2011-2022 走看看