• 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 lfloorfrac{n}{2} floor) ,则所有人得 (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) $
    • (sumlimits_{j=1}^4 p_{i,j} equiv 1 (mod 998244353)(1le i le n))

    各个测试点的性质如下

    测试点编号 n 特殊性质
    (1) (le 2)
    (2) (le 10) (p_{i,3}=p_{i,4}=0,(1le i le n))
    (3) (le 10)
    (4,5) (le 100) (p_{i,3}=p_{i,4}=0,(1le i le n))
    (6,7) (le 100) (p_{i,4}=0(1le i le n))
    (8,9,10,11) (le 100)
    (12sim20) (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
    ",(ans[i]+mod)%mod);
        return 0;
    }
    

    2018.10.21

  • 相关阅读:
    Github 简明教程--GitHub这么火,测试员你不学学吗?
    IT行业,尤其是软件测试,怎么才能月薪突破2万?
    linux 下cmake 编译 ,调用,调试 poco 1.6.0 小记
    ffmpeg(2.6) rockplayer android 下编译 小记.
    完成端口
    C++四种强制转换
    方法区(Method Area)基础知识
    逃逸分析
    堆空间参数设置小结
    堆中的线程私有缓存区域TLAB(Thread Local Allocation Buffer)
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9827273.html
Copyright © 2020-2023  润新知