传送门
有一副纸牌。牌一共有 n n n 种,分别标有 1 , 2 , . . . , n 1,2,...,n 1,2,...,n,每种有 C C C 张。故这副牌总共有 n C nC nC 张。
三张连号的牌 ( i , i + 1 , i + 2 ) (i,i+1,i+2) (i,i+1,i+2) 或三张相同的牌 ( i , i , i ) (i,i,i) (i,i,i) 可以组成一叠。如果一组牌可以分成若干(包括零)叠,就称其为一组王牌。
你从牌堆中摸了一些初始牌。现在你想挑出一些牌组成一组王牌,请问有多少种可能组成的王牌呢?答案对 998244353 998244353 998244353 取模。
两组牌相同当且仅当它们含有的每一种牌数量都相同。
数据范围: 1 ≤ n ≤ 1 0 18 1\leq n\leq 10^{18} 1≤n≤1018, 0 ≤ a i ≤ C ≤ 1000 0\leq a_i\leq C\leq 1000 0≤ai≤C≤1000, 0 ≤ X ≤ 1000 0\leq X\leq 1000 0≤X≤1000。注意 a i a_i ai 和 C C C 可能为 0 0 0。
为了方便,我们用 [ i ] [i] [i] 来表示 ( i , i + 1 , i + 2 ) (i,i+1,i+2) (i,i+1,i+2) 的牌, { i } \{i\} {i} 表示 ( i , i , i ) (i,i,i) (i,i,i) 的牌。
首先要注意到的一点是,以 i i i 开头的 [ i ] [i] [i] 数量 ≤ 2 \le 2 ≤2。因为一旦出现 3 3 3 个 [ i ] [i] [i] 就可以看成是 1 1 1 个 { i } \{i\} {i}, 1 1 1 个 { i + 1 } \{i+1\} {i+1} 和 1 1 1 个 { i + 2 } \{i+2\} {i+2} 的组合。
根据这一点,我们设 f i , j , k f_{i,j,k} fi,j,k 表示到第 i i i 种牌,有 j j j 个 [ i ] [i] [i],有 k k k 个 [ i − 1 ] [i-1] [i−1] 的方案数。
考虑转移。我们枚举 [ i ] [i] [i] 的数量(设有 l l l 个),则有(先不考虑有选择下限的牌):
f i − 1 , j , k × ( ⌊ C − j − k − l 3 ⌋ + 1 ) → f i , l , j f_{i-1,j,k}\times(\lfloor\frac{C-j-k-l}3\rfloor+1)\rightarrow f_{i,l,j} fi−1,j,k×(⌊3C−j−k−l⌋+1)→fi,l,j
意思就是,除去 i i i 在 “ “ “顺子 ” ” ”中用掉的牌数,剩下的牌凑 { i } \{i\} {i} 然后转移。
其实有下界是一样的,只不过可能要强制选一些 { i } \{i\} {i},特判一下即可。
注意到 n ≤ 1 0 18 n\le10^{18} n≤1018, O ( n ) O(n) O(n) d p dp dp 是不行的。但是发现转移方程大多是一样的,只有 X X X 个是需要特判的,所以矩阵快速幂优化就可以了。
PS:需要把后两维 ( j , k ) (j,k) (j,k) 压到一个状态,可以用 3 × j + k 3\times j+k 3×j+k 表示。
时间复杂度 O ( 9 3 X log n ) O(9^3X\log n) O(93Xlogn)。
#include
#define N 1005
#define ll long long
#define P 998244353
using namespace std;
ll n,k[N];
int C,X,a[N];
int add(int x,int y) {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y) {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y) {return (ll)x*y%P;}
struct matrix{
int m[9][9];
matrix(int t=0){
memset(m,0,sizeof(m));
for(int i=0;i<9;++i) m[i][i]=t;
}
friend matrix operator*(const matrix &A,const matrix &B){
matrix C(0);
for(int i=0;i<9;++i)
for(int k=0;k<9;++k)
for(int j=0;j<9;++j)
C.m[i][j]=add(C.m[i][j],mul(A.m[i][k],B.m[k][j]));
return C;
}
friend matrix operator^(matrix A,ll B){
matrix ans(1);
for(;B;B>>=1,A=A*A) if(B&1) ans=ans*A;
return ans;
}
}A,B,tmp;
void init(){
for(int i=0;i<=2;++i)
for(int j=0;j<=2;++j)
for(int k=0;k<=2;++k)
if(i+j+k<=C) B.m[j*3+k][i*3+j]=(C-i-j-k)/3+1;
}
int main(){
scanf("%lld%d%d",&n,&C,&X),init();
for(int i=1;i<=X;++i) scanf("%lld%d",&k[i],&a[i]);
A.m[0][0]=1;
for(int i=1;i<=X;++i){
A=A*(B^(k[i]-1-k[i-1]));
memset(tmp.m,0,sizeof(tmp.m));
for(int j=0;j<=2;++j){
for(int k=0;k<=2;++k){
for(int l=0;l<=2;++l){
int now=j+k+l,must=(now>a[i])?now:(a[i]+((now-a[i])%3+3)%3);
if(must<=C) tmp.m[j*3+k][l*3+j]=(C-must)/3+1;
}
}
}
A=A*tmp;
}
A=A*(B^(n-k[X]));
printf("%d\n",A.m[0][0]);
return 0;
}