[Luogu P2396] yyy loves Maths VII

洛谷传送门

题目背景

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

题目描述

一群同学在和yyy玩一个游戏

每次,他们会给yyy n n n张卡片,卡片上有数字,所有的数字都是"幸运数字",我们认为第 i i i张卡片上数字是 a i a_i ai

每次yyy可以选择向前走 a i a_i ai步并且丢掉第 i i i张卡片

当他手上没有卡片的时候他就赢了

但是呢,大家对"厄运数字"的位置布置下了陷阱,如果yyy停在这个格子上,那么他就输了

(注意:即使到了终点,但是这个位置是厄运数字,那么也输了)

现在,有些同学开始问:

yyy有多大的概率会赢呢?

大家觉得这是个好问题

有人立即让yyy写个程序

“电脑运行速度很快! 24 24 24的阶乘也不过就 620448401733239439360000 620448401733239439360000 620448401733239439360000,yyy你快写个程序来算一算”

yyy表示很无语,他表示他不想算概率,最多算算赢的方案数,而且是 % 1 , 000 , 000 , 007 \%1,000,000,007 %1,000,000,007以后的值

大家都不会写程序,只好妥协

但是这时候yyy为难了, 24 ! 24! 24!太大了,要跑好长时间.

他时间严重不够!需要你的帮助!

由于yyy人格分裂,某个数字可能既属于幸运数字又属于厄运数字。

输入输出格式

输入格式:

第一行 n n n

下面一行 n n n张卡片

第三行 m m m 表示yyy的厄运数字个数(最多 2 2 2个)

最后一行是 m m m个厄运数字

输出格式:

方案数 % 1 , 000 , 000 , 007 \%1,000,000,007 %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 % 10\% 10%的数据 n ≤ 10 n\le 10 n10

50 % 50\% 50%的数据 n ≤ 23 n\le 23 n23

100 % 100\% 100%的数据 n ≤ 24 n\le 24 n24

解题分析

卡常大状压…

因为 n ≤ 24 n\le 24 n24, 直接考虑枚举每种选择的状态暴力转移。 不过有个小技巧: 转移某个状态的时候直接用 l o w b i t lowbit lowbit计算前一个可以转移的位置, 这样做会枚举 2 23 2^{23} 223次, 就可以卡过去了…

代码如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define MOD 1000000007u
#define uint unsigned int
#define lbt(i) ((i) & (-(i)))
IN void add(uint &a, R int ad) {a += ad; if(a >= MOD) a -= MOD;}
uint dp[1 << 24], dis[1 << 24], bad[2], to[24];
int tot, bcnt;
int main(void)
{
    R int i, j, k, bd;
    scanf("%d", &tot); bd = (1 << tot) - 1;
    for (i = 0; i < tot; ++i) scanf("%d", &to[i]);
    scanf("%d", &bcnt); bad[0] = bad[1] = -1;
    for (i = 0; i < bcnt; ++i) scanf("%d", &bad[i]);
    dp[0] = 1;
    for (i = 1; i <= bd; ++i)
    {
        j = __builtin_ctz(i);
        dis[i] = dis[i - (1 << j)] + to[j];
        if(dis[i] == bad[0] || dis[i] == bad[1]) continue;
        j = i;
        W (j) k = lbt(j), add(dp[i], dp[i ^ k]), j ^= k;
    }
    printf("%u", dp[bd]);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值