1 条题解

  • 0
    @ 2025-8-24 21:37:14

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Siyuan
    Dream OIer.

    搬运于2025-08-24 21:37:14,当前版本为作者最后更新于2018-12-03 23:34:18,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    $$\Large\texttt{My Blog}$$


    题目链接:Luogu 2396

    yyy 有 nn 张卡片,每张卡片上都有一个数字 aia_i,每次 yyy 可以选择向前走 aia_i 步并丢弃第 ii 张卡片。当他手上没有卡片的时候他就赢了;但是有 mm 个“厄运数字”,在“厄运数字”的位置有陷阱,只要 yyy 停在这个格子上他就输了(即使终点是陷阱,那么也输了)。你需要算出 yyy 能赢的方案数,答案对 10000000071000000007 取模。

    数据范围:1n241\le n\le 240m20\le m\le 2


    Solution

    我们首先可以确定这是一道 DP\texttt{DP} 题目。由于并没有给出 aia_i 的具体范围,因此无法把距离设计进状态;又发现每张牌只能用一次并且没有顺序限制,观察到 nn 的范围也很小,我们可以考虑状压 DP\texttt{DP}

    我们定义 f[i]f[i] 表示使用了集合 ii 内的卡片有多少种赢的方案。转移时,我们记 dis[i]dis[i] 表示使用了集合 ii 内的卡片到达的位置,显然当 dis[i]dis[i] 为“厄运数字”时不能转移。否则我们枚举这次使用的卡片 jjjij\in i),那么有转移方程:$f[i]=\sum_{j=1}^n[j\in i]\cdot f[i\ \texttt{XOR}\ j]$(其中 i XOR ji\ \texttt{XOR}\ j 本质是从集合 ii 中删去元素 jj)。

    时间复杂度O(2nlogn)O(2^n\log n)


    Code

    #include <cstdio>
    
    const int N=24;
    const int mod=1e9+7;
    int n,m,b1,b2,dis[1<<N],f[1<<N];
    
    void upd(int &x,int y) {(x+=y)>=mod&&(x-=mod);}
    void solve(int x) {
        for(int i=x,j;i;i^=j) j=i&-i,upd(f[x],f[x^j]);
    }
    int main() {
        scanf("%d",&n);
        for(int i=0;i<n;++i) scanf("%d",&dis[1<<i]);
        scanf("%d",&m);
        if(m>0) scanf("%d",&b1);
        if(m>1) scanf("%d",&b2);
        f[0]=1;
        int msk=(1<<n)-1;
        for(int i=1;i<=msk;++i) {
            int j=i&-i;
            dis[i]=dis[i^j]+dis[j];
            if(dis[i]==b1||dis[i]==b2) continue;
            solve(i);
        }
        printf("%d\n",f[msk]);
        return 0;
    }
    
    • 1

    信息

    ID
    1419
    时间
    1000ms
    内存
    223MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者