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

题目

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

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

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

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

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

\(n\leq 10^{18},0\leq k\leq 10^3,0\leq a_i\leq m\leq 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+\begin{cases}\lfloor\frac{m-t}{3}\rfloor,a_n<t\\\lfloor\frac{m-t-3\lceil\frac{a_n-t}{3}\rceil}{3}\rfloor,otherwise\end{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]);
}
posted @ 2021-09-27 16:48  lemondinosaur  阅读(86)  评论(0)    收藏  举报