• ABC310F Make 10 Again


    ABC310F Make 10 Again

    [ABC310F] Make 10 Again

    题目大意

    你有 n n n个骰子,第 i i i个骰子能等概率掷出 1 ∼ A i 1\sim A_i 1Ai的点数。

    在同时掷出 n n n个骰子后,求满足下面条件的概率:

    • 能够在这些骰子中选若干个骰子,使得其点数和为 10 10 10

    输出概率模 998244353 998244353 998244353后的值。

    1 ≤ n ≤ 100 , 1 ≤ A i ≤ 1 0 6 1\leq n\leq 100,1\leq A_i\leq 10^6 1n100,1Ai106


    题解

    A i > 10 A_i>10 Ai>10时,大于 10 10 10的点数对凑 10 10 10没有用,所以我们记录点数小于等于 10 10 10的骰子的状态。

    f i , s f_{i,s} fi,s表示掷出了前 i i i个骰子,当前状态为 s s s的情况, s s s中存储了 1 ∼ 10 1\sim 10 110中每个数的个数。但是,如果这样的话,状态数为 10 0 10 100^{10} 10010,显然不行。不过,我们发现,如果有 11 11 11 1 1 1,我们只需要 10 10 10个,多出的一个不需要;如果有 6 6 6 2 2 2,我们只需要 5 5 5个,多出的一个也不需要。那么,对于 1 ≤ i ≤ 10 1\leq i\leq 10 1i10,我们最多只需要 ⌊ n i ⌋ \lfloor \dfrac ni\rfloor in i i i,因为如果需要超过 ⌊ n i ⌋ \lfloor \dfrac ni\rfloor in i i i,那么凑成的数一定是大于 10 10 10的。

    于是,我们可以将超过 ⌊ n i ⌋ \lfloor \dfrac ni\rfloor in i i i忽略。

    那么,总状态数 T = ∏ i = 1 10 ( ⌊ n i ⌋ + 1 ) T=\prod\limits_{i=1}^{10}(\lfloor\dfrac ni\rfloor+1) T=i=110(⌊in+1),其值为七万多。

    当骰子掷出 1 ≤ k ≤ 10 1\leq k\leq 10 1k10的点数时,在 s s s的对应状态中求加上一个 k k k(如果 k k k的数量已经到顶了,则不用加)后的状态 t t t,转移为

    f i , s + = f i − 1 , t × 1 A i f_{i,s}+=f_{i-1,t}\times \dfrac{1}{A_i} fi,s+=fi1,t×Ai1

    可以将每个 s s s k k k对应的 t t t预处理出,以优化这部分状态转移。

    当骰子掷出 k > 10 k>10 k>10的点数时(此时 A i > 10 A_i>10 Ai>10),这个骰子对凑 10 10 10没有作用,则转移为

    f i , s + = f i − 1 , s × A i − 10 A i f_{i,s}+=f_{i-1,s}\times \dfrac{A_i-10}{A_i} fi,s+=fi1,s×AiAi10

    最后,用背包判断每个状态 s s s能否能凑成 10 10 10(这里可以将背包数组改为一个二进制数,用右移和或来实现转移),再将能凑成 10 10 10的状态 s s s f n , s f_{n,s} fn,s统计在答案中即可。

    时间复杂度为 O ( 10 n T ) O(10nT) O(10nT)

    code

    #include
    using namespace std;
    const int N=1000000;
    const long long mod=998244353;
    int n,tot,bg,a[15],v[105],nxt[100005][11],to[11][6][4][3][3][1<<6];
    long long ans=0,jc[1000005],ny[1000005],f[105][100005];
    struct node{
    	int v1,v2,v3,v4,v5,z;
    }w[100005];
    long long mi(long long t,long long v){
    	if(!v) return 1;
    	long long re=mi(t,v/2);
    	re=re*re%mod;
    	if(v&1) re=re*t%mod;
    	return re;
    }
    void init(){
    	jc[0]=1;
    	for(int i=1;i<=N;i++) jc[i]=jc[i-1]*i%mod;
    	ny[N]=mi(jc[N],mod-2);
    	for(int i=N-1;i>=0;i--) ny[i]=ny[i+1]*(i+1)%mod;
    	for(int i=1;i<=N;i++) ny[i]=ny[i]*jc[i-1]%mod;
    }
    void dd(){
    	int z=(a[6]<<4)|(a[7]<<3)|(a[8]<<2)|(a[9]<<1)|a[10];
    	to[a[1]][a[2]][a[3]][a[4]][a[5]][z]=++tot;
    	w[tot]=(node){a[1],a[2],a[3],a[4],a[5],z};
    }
    void dfs(int t){
    	for(int i=0;i<=10/t;i++){
    		a[t]=i;
    		if(t<10) dfs(t+1);
    		else dd();
    	}
    }
    int pt(int s,int p){
    	node t=w[s];
    	if(p==1) t.v1=min(t.v1+1,10);
    	else if(p==2) t.v2=min(t.v2+1,5);
    	else if(p==3) t.v3=min(t.v3+1,3);
    	else if(p==4) t.v4=min(t.v4+1,2);
    	else if(p==5) t.v5=min(t.v5+1,2);
    	else t.z|=(1<<10-p);
    	return to[t.v1][t.v2][t.v3][t.v4][t.v5][t.z];
    }
    bool pd(int j){
    	bg=1;
    	for(int i=1;i<=w[j].v1;i++) bg|=(bg<<1);
    	for(int i=1;i<=w[j].v2;i++) bg|=(bg<<2);
    	for(int i=1;i<=w[j].v3;i++) bg|=(bg<<3);
    	for(int i=1;i<=w[j].v4;i++) bg|=(bg<<4);
    	for(int i=1;i<=w[j].v5;i++) bg|=(bg<<5);
    	if(bg&(1<<10)) return 1;
    	for(int i=6;i<=10;i++){
    		if((w[j].z&(1<<10-i))&&(bg&(1<<10-i))) return 1;
    	}
    	return 0;
    }
    int main()
    {
    	init();
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++){
    		scanf("%d",&v[i]);
    	}
    	dfs(1);
    	f[0][1]=1;
    	for(int j=1;j<=tot;j++){
    		for(int k=1;k<=10;k++){
    			nxt[j][k]=pt(j,k);
    		}
    	}
    	for(int i=1;i<=n;i++){
    		for(int j=1;j<=tot;j++){
    			if(!f[i-1][j]) continue;
    			for(int k=1;k<=min(10,v[i]);k++){
    				f[i][nxt[j][k]]=(f[i][nxt[j][k]]+f[i-1][j]*ny[v[i]])%mod;
    			}
    			if(v[i]>10){
    				f[i][j]=(f[i][j]+f[i-1][j]*ny[v[i]]%mod*(v[i]-10))%mod;
    			}
    		}
    	}
    	for(int j=1;j<=tot;j++){
    		if(pd(j)) ans=(ans+f[n][j])%mod;
    	}
    	printf("%lld",ans);
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
  • 相关阅读:
    最新 Qt FFmpeg 环境配置
    上位机在自动化中有何作用和优势?
    Python的Pandas库(一)基础使用
    《DevOps实践指南》- 读书笔记(三)
    C++ list 的使用
    (Qt) 子组件绘制QPainter
    Java项目:SSM宠物商城带后台管理系统
    痛苦皆来自于认知的局限
    Viva Employee Communications & Communities部署方案
    Rabin-Karp字符串搜索简介
  • 原文地址:https://blog.csdn.net/tanjunming2020/article/details/133410792