[洛谷2396]yyy loves Maths VII 题解

[洛谷2396]yyy loves Maths VII 题解

题目地址:洛谷:【P2396】yyy loves Maths VII – 洛谷

题目背景

yyy对某些数字有着情有独钟的喜爱,他叫他们为幸运数字;然而他作死太多,所以把自己讨厌的数字成为”厄运数字”

题目描述

一群同学在和yyy玩一个游戏
每次,他们会给yyy n张卡片,卡片上有数字,所有的数字都是”幸运数字”,我们认为第i张卡片上数字是ai
每次yyy可以选择向前走ai步并且丢掉第i张卡片
当他手上没有卡片的时候他就赢了
但是呢,大家对”厄运数字”的位置布置下了陷阱,如果yyy停在这个格子上,那么他就输了
(注意:即使到了终点,但是这个位置是厄运数字,那么也输了)
现在,有些同学开始问:
yyy有多大的概率会赢呢?
大家觉得这是个好问题
有人立即让yyy写个程序
“电脑运行速度很快!24的阶乘也不过就620448401733239439360000,yyy你快写个程序来算一算”
yyy表示很无语,他表示他不想算概率,最多算算赢的方案数,而且是%1,000,000,007以后的值
大家都不会写程序,只好妥协
但是这时候yyy为难了,24!太大了,要跑好长时间.
他时间严重不够!需要你的帮助!
由于yyy人格分裂,某个数字可能既属于幸运数字又属于厄运数字。

输入输出格式

输入格式:
第一行n
下面一行n张卡片
第三行m 表示yyy的厄运数字个数(最多2个)
最后一行是m个厄运数字

输出格式:
方案数%1,000,000,007

输入输出样例

输入样例#1:

8
1 3 1 5 2 2 2 3
0

输出样例#1:

40320

输入样例#2:

24
1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
2
10 15

输出样例#2:

0

说明

数据范围:
10%的数据n<=10
50%的数据n<=23
100%的数据n<=24

题解

勉强算可以状压吧。
考虑状压DP统计方案数,方程比较容易想出
\displaystyle dp[S] = \sum_{i \in S, i \neq \text{厄运}} dp[S \backslash \{i\}]
我们预处理出数组dis[S]表示S这些卡片的和,方便判断是否可以转移。
复杂度是O(2^n)。然而很卡常,所以还是开O2吧23333。

代码

// Code by KSkun, 2018/5
#include <cstdio>
#include <cctype>

typedef long long LL;

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

inline LL readint() {
    register LL res = 0, neg = 1;
    register char c = fgc();
    while(!isdigit(c)) {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(isdigit(c)) {
        res = (res << 1) + (res << 3) + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = (1 << 24) + 5, MO = 1e9 + 7;

inline int lowbit(int x) {
    return x & -x;
}

int n, m, num[5], dis[MAXN], dp[MAXN];

int main() {
    n = readint();
    for(register int i = 1; i <= n; i++) {
        dis[1 << (i - 1)] = readint();
    }
    m = readint();
    for(register int i = 1; i <= m; i++) {
        num[i] = readint();
    }
    for(register int i = 1; i < 1 << n; i++) {
        register int j = lowbit(i);
        dis[i] = dis[i ^ j] + dis[j];
    }
    dp[0] = 1;
    for(register int i = 1; i < 1 << n; i++) {
        register bool can = true;
        for(register int j = 1; j <= m; j++) {
            if(dis[i] == num[j]) {
                can = false; break;
            }
        }
        if(!can) continue;
        for(register int j = i, k = lowbit(j); j; j -= k, k = lowbit(j)) {
            dp[i] += dp[i ^ k];
            if(dp[i] >= MO) dp[i] -= MO;
        }
    }
    printf("%d", dp[(1 << n) - 1]);
    return 0;
}


发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据