20200617 SCOI模拟T1(状压dp)

T1 P4460 [CQOI2018]解锁屏幕

思路:
先看数据范围,想到状压
发现最后一个到达的点对 dp 有影响,于是记录一下
d p [ i ] [ j ] dp[i][j] dp[i][j] 表示状态为 i,最后一个点为 j 的状态数
如果 jk 点间的点全部走过,有转移方程
d p [ i ∣ ( 1 < < k ) ] [ k ] + = d p [ i ] [ j ] dp[i|(1<<k)][k]+=dp[i][j] dp[i(1<<k)][k]+=dp[i][j]
i 较大的状态只能由较小的状态转移来,可以循环模拟 DFS
发现暴力判断会 TLE,于是考虑预处理
时间复杂度 O ( 2 n n 2 ) O(2^nn^2) O(2nn2)

代码:

#include <bits/stdc++.h>
using namespace std;
#define in Read()

inline char ch() {
  static char buf[1 << 21], *p1 = buf, *p2 = buf;
  return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2)
             ? EOF
             : *p1++;
}

inline int in {
  int s = 0, f = 1;
  char x;
  for (x = ch(); x < '0' || x > '9'; x = ch())
    if (x == '-') f = -1;
  for (; x >= '0' && x <= '9'; x = ch()) s = (s * 10) + (x & 15);
  return f == 1 ? s : -s;
}

const int A = 25;
const int mod = 1e8 + 7;
int n;
struct Point {
  int x, y;
  Point(int u = 0, int v = 0) { x = u, y = v; }
  inline Point friend operator-(const Point u, const Point v) {
    return Point(v.x - u.x, v.y - u.y);
  }
  inline int friend operator^(const Point u, const Point v) {
    return u.x * v.y - u.y * v.x;
  }
} p[A];
int d[A][A];
int f[1 << 21][A];
int res = 0;

inline int check(Point u, Point v, Point w) {
  if (((v - u) ^ (w - u)) != 0) return 0;
  if (u.x > v.x) swap(u, v);
  if (w.x < u.x || w.x > v.x) return 0;
  if (u.y > v.y) swap(u, v);
  if (w.y < u.y || w.y > v.y) return 0;
  return 1;
}

inline int find(int now) {
  int num = 0;
  for (int i = 0; i < n; i++)
    if (now & (1 << i)) num++;
  return num;
}

signed main() {
  n = in;
  for (int i = 0; i < n; i++) p[i].x = in, p[i].y = in;
  for (int i = 0; i < n; i++) {
    for (int j = i + 1; j < n; j++) {
      for (int k = j + 1; k < n; k++) {
        if (check(p[i], p[j], p[k])) d[i][j] |= (1 << k), d[j][i] |= (1 << k);
        if (check(p[i], p[k], p[j])) d[i][k] |= (1 << j), d[k][i] |= (1 << j);
        if (check(p[j], p[k], p[i])) d[j][k] |= (1 << i), d[k][j] |= (1 << i);
      }
    }
  }
  for (int i = 0; i < n; i++) f[1 << i][i] = 1;
  for (int i = 1; i < (1 << n); i++) {
    for (int j = 0; j < n; j++) {
      if ((i & (1 << j)) == 0) continue;
      for (int k = 0; k < n; k++) {
        if (i & (1 << k)) continue;
        if ((i & d[j][k]) != d[j][k]) continue;
        f[i | (1 << k)][k] = (f[i | (1 << k)][k] + f[i][j]) % mod;
      }
    }
  }
  for (int i = 1; i < (1 << n); i++) {
    if (find(i) < 4) continue;
    for (int j = 0; j < n; j++) {
      if ((i & (1 << j)) == 0) continue;
      res = (res + f[i][j]) % mod;
    }
  }
  printf("%d\n", res);
  return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值