nowcoder 提高组模拟赛 选择题 解题报告

选择题

链接:

https://www.nowcoder.com/acm/contest/178/B

来源:牛客网

题目描述

有一道选择题,有 \(a,b,c,d\) 四个选项。

现在有 \(n\) 个人来做这题,第 \(i\) 个人有 \(p_{i,j}\) 的概率选第 \(j\) 个选项。

定义 \(cnt(x)\) 为选第 \(x\) 个选项的人数。

\(mx\)\(cnt(x)\) 最大的 \(x\) (如果有多个\(cnt(x)\)最大的 \(x\),则取其中 \(x\) 最小的),

\(cnt(mx) \le \lfloor\frac{n}{2}\rfloor\) ,则所有人得 \(0\) 分;

否则令 \(choice_i\) 表示第 \(i\) 个人选的选项,则第 \(i\) 人得\(w_{mx,choice_i}\)

求每个人的期望得分。

输入描述:

第一行一个整数 \(n\) ,表示人数。

接下来 \(n\) 行,每行 \(4\) 个整数,其中第 \(i\) 行第 \(j\) 个数表示 \(p_{i,j}\) ,即在模 \(998244353\) 意义下第 \(i\) 个人选第 \(j\) 个选项的概率。

接下来 \(4\) 行,每行 \(4\) 个整数,第 \(i\) 行第 \(j\) 个数表示 \(w_{i,j}\)

输出描述:

\(n\) 行,第 \(i\) 行表示第 \(i\) 个人在模 \(998244353\) 意义下的期望得分。

备注:

全部的输入数据满足:

  • \(1 ≤ n ≤ 2000\)
  • $0 ≤ p_{i,j} < 998244353 (1 ≤ i ≤ n,1 ≤ j ≤ 4) $
  • \(\sum\limits_{j=1}^4 p_{i,j} \equiv 1 (\bmod 998244353)(1\le i \le n)\)

各个测试点的性质如下

测试点编号 n 特殊性质
\(1\) \(\le 2\)
\(2\) \(\le 10\) \(p_{i,3}=p_{i,4}=0,(1\le i \le n)\)
\(3\) \(\le 10\)
\(4,5\) \(\le 100\) \(p_{i,3}=p_{i,4}=0,(1\le i \le n)\)
\(6,7\) \(\le 100\) \(p_{i,4}=0(1\le i \le n)\)
\(8,9,10,11\) \(\le 100\)
\(12\sim20\) \(\le 2000\)

Solution

考试的时候打了55pts暴力,结果爆5了,原因竟然是出负数了没模正。。

枚举每个人选什么和最后结果是什么,然后算一下其他人的选择对这个结果的概率。

每次可以简单的\(n^2\)\(dp\)

\(dp_{i,j}\)代表前\(i\)个人有\(j\)个选了的和

\(dp_{i,j}=dp_{i-1,j-1}p_{i,k}+dp_{i-1,j}(1-p_{i,k})\)

发现每次只是少了一个人不进行\(dp\)

可以先把所有人的\(dp\)搞出来,然后\(O(n)\)把人踢出来

具体的,设\(dp_{i}\)\(i\)个人选了某选项的全集

枚举每个人时,显然有\(dp_i'=p_idp_{i-1}+(1-p_i)dp_i\)

那么回退时有\(dp_i=\frac{dp_i'-p_idp_{i-1}}{1-p_i}\)

注意压维了的话是存在顺序的,还要特判\(p_i=1\)


Code:

#include <cstdio>
#define ll long long
const int N=2e3+10;
const ll mod=998244353ll;
int n;
ll p[N][5],w[5][5],dp[N][5],inv[N][5],ans[N];
ll quickpow(ll d,ll k)
{
    ll f=1;
    d=(d%mod+mod)%mod;
    while(k)
    {
        if(k&1) f=f*d%mod;
        d=d*d%mod;
        k>>=1;
    }
    return f;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=4;j++)
            scanf("%lld",&p[i][j]),inv[i][j]=quickpow(1-p[i][j],mod-2);
    for(int i=1;i<=4;i++)
        for(int j=1;j<=4;j++)
            scanf("%lld",&w[i][j]);
    dp[0][1]=dp[0][2]=dp[0][3]=dp[0][4]=1;
    for(int k=1;k<=4;k++)
        for(int i=1;i<=n;i++)
        {
            ll p1=(1-p[i][k]),p2=p[i][k];
            for(int j=n;j;j--)
                dp[j][k]=(p2*dp[j-1][k]+p1*dp[j][k])%mod;
            dp[0][k]=dp[0][k]*(1-p[i][k])%mod;
        }
    for(int i=1;i<=n;i++)//这个人
        for(int j=1;j<=4;j++)//选啥
        {
            if(p[i][j]==0) continue;
            for(int k=1;k<=4;k++)//结果是
            {
                if(w[k][j]==0) continue;
                int flag=1,up=(n>>1)+1-(j==k);
                ll sum=0;
                if(p[i][k]==1) flag=0;
                if(flag)
                {
                    dp[0][k]=dp[0][k]*inv[i][k]%mod;
                    ll p2=p[i][k];
                    for(int l=1;l<up;l++)//退包
                        dp[l][k]=(dp[l][k]-p2*dp[l-1][k])%mod*inv[i][k]%mod;
                    for(int l=up;l<=n;l++)
                    {
                        dp[l][k]=(dp[l][k]-p2*dp[l-1][k])%mod*inv[i][k]%mod;
                        sum+=dp[l][k];
                    }
                }
                if(!flag)
                {
                    for(int i=up+1;i<=n;i++)
                        sum+=dp[i][k];
                }
                sum%=mod;
                (ans[i]+=w[k][j]*sum%mod*p[i][j])%=mod;
                if(flag)
                {
                    ll p1=(1-p[i][k]),p2=p[i][k];
                    for(int l=n;l;l--)
                        dp[l][k]=(dp[l][k]*p1+p2*dp[l-1][k])%mod;
                    dp[0][k]=dp[0][k]*(1-p[i][k])%mod;
                }
            }
        }
    for(int i=1;i<=n;i++)
        printf("%lld\n",(ans[i]+mod)%mod);
    return 0;
}

2018.10.21

猜你喜欢

转载自www.cnblogs.com/ppprseter/p/9827273.html