ABC310F题解

文章目录

  • [Make 10 Again](https://atcoder.jp/contests/abc310/tasks/abc310_f)
    • 问题建模
    • 问题分析
      • 1.从集合的角度思考,所求与骰子的关系
      • 2.明确骰子作用后,采用DP的做法,动态考虑到当前骰子所做贡献
      • 代码

Make 10 Again

ABC310F题解_第1张图片
ABC310F题解_第2张图片

问题建模

给定n个骰子,每个骰子有可以投出1~ai内的数,这n个骰子投出来的数中任选几个数能组成10的概率有多少。

问题分析

1.从集合的角度思考,所求与骰子的关系

题目所求实际上就是再问,有n个可任选至多1个数的数集,能产生多少个包含10的数集。

一个骰子可以投出内的数,也就是说,一个骰子可以贡献出一个包含{1~ai}的可选数集,则当前骰子数集内的每一个数被选出后参与前面的骰子所产生的数集,从而直接或者间接提供所求的数集。

2.明确骰子作用后,采用DP的做法,动态考虑到当前骰子所做贡献

当到第i个骰子后,枚举第i个骰子所产生数集里的每一个数,考虑当该数参与前面骰子所能产生所有数集的情况。情况分两种,一种是该数加上数集内的数,另一种是不加,两种情况所产生的数集取并集且与{0~10}的数集取交集,这样得到的一个数集即为对我们有用的数集,里面的数都小于等于1。

由于对我们有用的数集只在{0~10},则可以用二进制表示这样的数集,则对于每一个骰子所产生的每一个数我们都通过遍历一遍0到 2 11 − 1 2^{11}-1 2111来考虑该数对所有有用数集的贡献即可。

由于有些骰子所产生的数集中一些数大于10,则该数出现时,必然只选择前面满足条件的数集,而不考虑该数,故大于10的数的情况则是让前面的数集出现次数+1。

代码

#include

#define x first
#define y second
#define C(i) str[0][i]!=str[1][i]
using namespace std;
typedef unsigned long long ULL;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
const int N = 105, Mod=998244353,P=2048;
LL a[N];
LL f[N][2050];

LL qmi(LL m,LL k,LL p){
    LL res=1%p,t=m;
    while(k){
        if(k&1) res=((LL)res*t)%p;
        t=(LL)t*t%p; 
        k>>=1;
    }
    return res;
}

void solve() {
    int n;
    cin >>n;
    for(int i=1;i<=n;i++)  cin >>a[i];
    f[0][1]=1;///初始时,投掷0个骰子,数集内仅有0可得,故2^0对应的情况有一种	
    LL sum=1;
    for(int i=1;i<=n;i++){
        sum=((LL)sum*a[i])%Mod;//总情况
        for(int j=0;j<P;j++){
            for(int k=1;k<=min(a[i],(LL)10);k++){
                ///(j|j<
                f[i][(j|j<<k)%P]=(f[i][(j|j<<k)%P]+f[i-1][j])%Mod;
            }
            if(a[i]>10) f[i][j]=(f[i][j]+(LL)f[i-1][j]*(a[i]-10))%Mod;
        }
    }
    LL ans=0;
    ///包含10的数集的情况都要累加加来
    for(int j=1024;j<P;j++) ans=(ans+f[n][j])%Mod;
    cout <<ans*qmi(sum,Mod-2,Mod)%Mod<<"\n";
}

int main() {
    int t = 1;
    //cin >> t;
    while (t--) solve();
    return 0;
}

你可能感兴趣的:(ABC,算法,动态规划)