1. 程式人生 > >[Luogu P2396] yyy loves Maths VII

[Luogu P2396] yyy loves Maths VII

洛谷傳送門

題目背景

yyy對某些數字有著情有獨鍾的喜愛,他叫他們為幸運數字;然而他作死太多,所以把自己討厭的數字成為"厄運數字"

題目描述

一群同學在和yyy玩一個遊戲

每次,他們會給yyy nn張卡片,卡片上有數字,所有的數字都是"幸運數字",我們認為第ii張卡片上數字是aia_i

每次yyy可以選擇向前走aia_i步並且丟掉第ii張卡片

當他手上沒有卡片的時候他就贏了

但是呢,大家對"厄運數字"的位置佈置下了陷阱,如果yyy停在這個格子上,那麼他就輸了

(注意:即使到了終點,但是這個位置是厄運數字,那麼也輸了)

現在,有些同學開始問:

yyy有多大的概率會贏呢?

大家覺得這是個好問題

有人立即讓yyy寫個程式

“電腦執行速度很快!2424的階乘也不過就620448401733239439360000620448401733239439360000,yyy你快寫個程式來算一算”

yyy表示很無語,他表示他不想算概率,最多算算贏的方案數,而且是%1,000,000,007\%1,000,000,007以後的值

大家都不會寫程式,只好妥協

但是這時候yyy為難了,24!24!太大了,要跑好長時間.

他時間嚴重不夠!需要你的幫助!

由於yyy人格分裂,某個數字可能既屬於幸運數字又屬於厄運數字。

輸入輸出格式

輸入格式:

第一行nn

下面一行nn張卡片

第三行mm 表示yyy的厄運數字個數(最多2

2個)

最後一行是mm個厄運數字

輸出格式:

方案數%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\%的資料n10n\le 10

50%50\%的資料n23n\le 23

100%100\%的資料n24n\le 24

解題分析

卡常大狀壓…

因為n24n\le 24, 直接考慮列舉每種選擇的狀態暴力轉移。 不過有個小技巧: 轉移某個狀態的時候直接用l

owbitlowbit計算前一個可以轉移的位置, 這樣做會列舉2232^{23}次, 就可以卡過去了…

程式碼如下:

#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]);
}