[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
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.
解释:设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; }