• #dp,矩阵乘法#洛谷 5371 [SNOI2019]纸牌


    题目

    一副纸牌有 (n) 种,每种有 (m) 张,

    现在有 (k) 个限制条件形如第 (k_i) 种牌至少选 (a_i) 张,

    一个三元组合法当且仅当其为 ((i,i+1,i+2))((i,i,i))

    现在问有多少种方案使得正好可以分成若干个三元组,

    方案不同当且仅当选择的种类不同或数量不同

    (nleq 10^{18},0leq kleq 10^3,0leq a_ileq mleq 10^3)


    分析

    考虑三个 ((i,i+1,i+2)) 可以用 ((i,i,i))((i+1,i+1,i+1))((i+2,i+2,i+2)) 代替,

    所以这样的三元组本质上最多出现两次,设 (dp[n][i][j]) 表示

    (n) 个其中 (i) 个作为 ((n-1,n,n+1))(j) 个作为 ((n,n+1,n+2)) 的方案数。

    通过 (a_n)(t=i+j+k) 的大小分为两种情况,则

    [large dp[n][i][j]=sum_{k=0}^2dp[n-1][j][k]+1+egin{cases}lfloorfrac{m-t}{3} floor,a_n<t\lfloorfrac{m-t-3lceilfrac{a_n-t}{3} ceil}{3} floor,otherwiseend{cases} ]

    加一是因为要考虑选完 (a_n) 之后不选 ((n,n,n)) 的情况,

    当没有限制条件的时候直接矩阵乘法,否则直接求出转移矩阵相乘即可

    最后答案为 (dp[n][0][0])


    代码

    #include <cstdio>
    #include <cctype>
    #define rr register
    using namespace std;
    const int mod=998244353; int m;
    struct maix{int p[9][9];}B,A,ANS,U;
    typedef long long lll; lll n;
    inline lll iut(){
    	rr lll ans=0; rr char c=getchar();
    	while (!isdigit(c)) c=getchar();
    	while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
    	return ans;
    }
    inline maix mul(maix A,maix B){
    	rr maix C;
    	for (rr int i=0;i<9;++i)
    	for (rr int j=0;j<9;++j){
    		C.p[i][j]=0;
    		for (rr int k=0;k<9;++k)
    		    C.p[i][j]=(C.p[i][j]+1ll*A.p[i][k]*B.p[k][j]%mod)%mod;
    	}
    	return C;
    }
    inline maix ksm(maix A,lll y){
    	rr maix ANS=U;
    	for (;y;y>>=1,A=mul(A,A))
    	    if (y&1) ANS=mul(ANS,A);
        return ANS;
    }
    signed main(){
    	for (rr int i=0;i<9;++i) U.p[i][i]=1;
    	n=iut(),m=iut(),ANS.p[0][0]=1;
    	for (rr int i=0;i<3;++i)
    	for (rr int j=0;j<3;++j)
    	for (rr int k=0;k<3;++k) if (i+j+k<=m)
    	    A.p[j*3+k][i*3+j]=(m-i-j-k)/3+1;
    	rr lll lst=0,x,y;
    	for (rr int Q=iut();Q;--Q,lst=x){
    		x=iut(),y=iut(),ANS=mul(ANS,ksm(A,x-lst-1));
    		for (rr int i=0;i<9;++i)
    		for (rr int j=0;j<9;++j) B.p[i][j]=0;
    		for (rr int i=0;i<3;++i)
    		for (rr int j=0;j<3;++j)
    		for (rr int k=0;k<3;++k){
    			rr int now=i+j+k;
    			if (y>=i+j+k) now+=(y-i-j-k+2)/3*3;
    			if (now<=m) B.p[j*3+k][i*3+j]=(m-now)/3+1;
    		}
    		ANS=mul(ANS,B);
    	}
    	ANS=mul(ANS,ksm(A,n-lst));
    	return !printf("%d",ANS.p[0][0]);
    }
    
  • 相关阅读:
    【Spring源码解读】bean标签中的属性(二)你可能还不够了解的 abstract 属性和 parent 属性
    【效率工具】史上最好用的SSH一键登录脚本,第三版更新!
    WebGL简易教程——目录
    写技术博客的一些心得体会
    空间直线与球面相交算法
    three.js中帧缓存的使用
    curl使用小记(二)——远程下载一张图片
    curl使用小记(一)
    three.js中场景模糊、纹理失真的问题
    关于three.js中的矩阵更新
  • 原文地址:https://www.cnblogs.com/Spare-No-Effort/p/15343657.html
Copyright © 2020-2023  润新知