NOI模拟(5.4) CQOID2T1 解锁屏幕

解锁屏幕

题目背景:

5.4 模拟 CQOI2018D2T1  

分析:状压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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值