题解:CF2140E Prime Gaming

E2 跑了 2859 ms,怎么感觉有点蓟县。


E1

首先 m=1m = 1m=1 的时候只有一种全为 111 的情况,答案为 111

接下来只需考虑 m=2m = 2m=2 的情况。由于 n≤20n \le 20n20,考虑状压。

先钦定从左往右数第 iii 堆石头的信息存在长度为 nnn 二进制从高位往低位数的第 iii 位上。由于 ci=1/2c_i = 1/2ci=1/2,我们设二进制某一位为 0/10/10/1 表示石头的数量为 1/21/21/2

dpi,S,0/1dp_{i,S,0/1}dpi,S,0/1 表示有 iii 堆石子,状态为 SSS 且当前 Alice 为先手/后手的时候最终的那一堆石头的数量是否能为 222。则有转移方程:

{dpi,S,0=⋁S′∈F(S)dpi−1,S′,1dpi,S,1=⋀S′∈F(S)dpi−1,S′,0 \begin{cases} dp_{i,S,0} = \bigvee_{S' \in \mathcal{F}(S)} dp_{i-1,S',1}\\ dp_{i,S,1} = \bigwedge_{S' \in \mathcal{F}(S)} dp_{i-1,S',0} \end{cases} {dpi,S,0=SF(S)dpi1,S,1dpi,S,1=SF(S)dpi1,S,0

其中 F(S)\mathcal{F}(S)F(S) 表示 SSS 中的某一位去掉以后的状态,这个可以进行预处理。

nxtS,inxt_{S,i}nxtS,i 表示状态为 SSS 去掉第 iii 堆石头时形成的状态。但需要注意,这个状态 SSS 在二进制下的长度一直是 nnn,因此即使没有该位置的信息,直接置为 000 即可。由于是从高位往低位存储信息的,因此是形如 xxxxoyy→xxxxyy\texttt{xxxxoyy} \to \texttt{xxxxyy}xxxxoyyxxxxyy 的合并。显然可以分为两个部分合并,写成如下式子(注意 iii 下标是 0-base 的):

nxts,i=(⌊S2n−i⌋⋅2n−i)+(2⋅(S mod 2n−i−1)) nxt_{s,i} = \left( \left\lfloor \frac{S}{2^{n-i}} \right\rfloor \cdot 2^{n-i} \right) + \left( 2 \cdot \left( S \bmod 2^{n-i-1} \right) \right) nxts,i=(2niS2ni)+(2(Smod2ni1))

也就是等价于如下表达式:

nxt[S][i] = ((S >> (n - i)) << (n - i)) | ((S & ((1 << (n - i - 1)) - 1)) << 1);

最后的答案为 2n+∑S=02n−1dpn,S,02^n + \sum \limits_{S = 0}^{2^n - 1} dp_{n,S,0}2n+S=02n1dpn,S,0

代码如下:

void solve ()
{
    int n = read (),m = read (),k = read (),ans = 0;
    vector <int> fl (n);
    for (int i = 1;i <= k;++i) fl[read () - 1] = 1;
    if (m == 1) {puts ("1");return;}
    vector <vector <int>> dp (1 << n,vector <int> (2,0)),ndp (1 << n,vector <int> (2,0)),nxt (1 << n,vector <int> (n + 1));
    for (int S = 0;S < (1 << n);++S)
        for (int i = 0;i < n;++i) nxt[S][i] = ((S >> (n - i)) << (n - i)) | ((S & ((1 << (n - i - 1)) - 1)) << 1);
    dp[0][0] = dp[0][1] = 0;dp[1 << (n - 1)][0] = dp[1 << (n - 1)][1] = 1;
    for (int i = 2;i <= n;++i)
    {
        for (int S = 0;S < (1 << i);++S)
        {
            ndp[S << (n - i)][0] = 0,ndp[S << (n - i)][1] = 1;
            for (int j = 0;j < i;++j) 
                if (fl[j]) ndp[S << (n - i)][0] |= dp[nxt[S << (n - i)][j]][1],ndp[S << (n - i)][1] &= dp[nxt[S << (n - i)][j]][0];
        }
        for (int S = 0;S < (1 << i);++S) dp[S << (n - i)][0] = ndp[S << (n - i)][0],dp[S << (n - i)][1] = ndp[S << (n - i)][1];
    }
    for (int S = 0;S < (1 << n);++S) ans += dp[S][0];
    printf ("%d\n",ans + (1 << n));
}

E2

在 E1 的基础上,我们考虑一下设计的状态所表示的信息。设 dpi,S,0/1dp_{i,S,0/1}dpi,S,0/1 表示当在 SSS 的状态下,这些位置的石头数量均不小于 kkk 时,最后的一堆的石头数是否能不小于 kkk

答案统计的时候,枚举 kkkiii,表示 nnn 堆石头中有 iii 堆的石头数量为 [k,m][k,m][k,m]n−in - ini 堆的石头数量为 [1,k−1][1,k - 1][1,k1],则 ∑S=02n−1⟦pop_count⁡(S)=i⟧⋅dpn,S,0×(m−k+1)i×(k−1)n−i\sum\limits_{S = 0}^{2^n - 1} \llbracket \operatorname{pop\_count}(S) = i \rrbracket \cdot dp_{n,S,0} \times (m - k + 1)^i \times (k - 1)^{n - i}S=02n1[[pop_count(S)=i]]dpn,S,0×(mk+1)i×(k1)ni 表示答案不小于 iii 的合法数量。显然最后一堆石头的数量为 xxx 时的贡献会被拆成 1,2,⋯ ,x1,2,\cdots,x1,2,,xxxx 个地方的单个 111 的贡献,因此最后的答案为:

∑i=0n∑S=02n−1⟦pop_count⁡(S)=i⟧⋅dpn,S,0×(m−k+1)i×(k−1)n−i \sum \limits_{i = 0}^n\sum_{S = 0}^{2^n - 1} \llbracket \operatorname{pop\_count}(S) = i \rrbracket \cdot dp_{n,S,0} \times (m - k + 1)^i \times (k - 1)^{n - i} i=0nS=02n1[[pop_count(S)=i]]dpn,S,0×(mk+1)i×(k1)ni

完整代码如下:

void solve ()
{
    int n = read (),m = read (),k = read ();ll ans = 0;
    vector <int> fl (n);
    for (int i = 1;i <= k;++i) fl[read () - 1] = 1;
    vector <vector <int>> dp (1 << n,vector <int> (2,0)),ndp (1 << n,vector <int> (2,0)),nxt (1 << n,vector <int> (n + 1));
    vector <int> cnt (n + 1,0);
    for (int S = 0;S < (1 << n);++S)
        for (int i = 0;i < n;++i) nxt[S][i] = ((S >> (n - i)) << (n - i)) | ((S & ((1 << (n - i - 1)) - 1)) << 1);
    dp[0][0] = dp[0][1] = 0;dp[1 << (n - 1)][0] = dp[1 << (n - 1)][1] = 1;
    cnt[1] = 1;
    for (int i = 2;i <= n;++i)
    {
        for (int j = 0;j <= n;++j) cnt[j] = 0;
        for (int S = 0;S < (1 << i);++S)
        {
            ndp[S << (n - i)][0] = 0,ndp[S << (n - i)][1] = 1;
            for (int j = 0;j < i;++j) 
                if (fl[j]) ndp[S << (n - i)][0] |= dp[nxt[S << (n - i)][j]][1],ndp[S << (n - i)][1] &= dp[nxt[S << (n - i)][j]][0];
        }
        for (int S = 0;S < (1 << i);++S) 
        {
            dp[S << (n - i)][0] = ndp[S << (n - i)][0],dp[S << (n - i)][1] = ndp[S << (n - i)][1];
            int tot = __builtin_popcount (S);
            cnt[tot] = (cnt[tot] + dp[S << (n - i)][0]) % MOD;
        }
    }
    for (int i = 0;i <= n;++i)
        for (int k = 1;k <= m;++k) ans = (ans + qpow (m - k + 1,i) * qpow (k - 1,n - i) % MOD * cnt[i]) % MOD;
    printf ("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值