正题
题目链接:https://www.luogu.com.cn/problem/P5371
题目大意
有nnn种牌,每种牌最多CCC张,XXX个限制形如kik_iki种牌至少aia_iai张。
求所有牌的序号能分成(i,i,i)(i,i,i)(i,i,i)或者(i,i+1,i+2)(i,i+1,i+2)(i,i+1,i+2)的若干组的方案数。
1≤n≤1018,0≤X,C≤10001\leq n\leq 10^{18},0\leq X,C\leq 10001≤n≤1018,0≤X,C≤1000
解题思路
看到这个nnn的范围考虑矩阵乘法,然后考虑上面那个叠的东西,因为(i,i,i)(i,i,i)(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),所以这样的话不难发现一个牌最多有666张由前或后构成一叠,再进一步的,设fi,jf_{i,j}fi,j表示上个选了iii张,这一个选了i+ji+ji+j张,此时有i,j≤3i,j\leq 3i,j≤3,这样状态数就是999了,然后暴力矩阵乘法,局部暴力即可。
时间复杂度:O(X93logn+XC)O(X9^3\log n+XC)O(X93logn+XC)
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll S=9,P=998244353;
struct Matrix{ll a[S][S];
}c,ans,f;
ll n,m,C;
Matrix operator*(const Matrix &a,const Matrix &b){memset(c.a,0,sizeof(c.a));for(ll i=0;i<S;i++) for(ll j=0;j<S;j++)for(ll k=0;k<S;k++)(c.a[i][j]+=a.a[i][k]*b.a[k][j]%P)%=P;return c;
}
Matrix Solve(ll k){memset(c.a,0,sizeof(c.a));for(ll i=0;i<9;i++){for(ll j=k;j<=C;j++){ll x=i/3,y=x+i%3,z=j;if(z<y)continue;y-=x;z-=x;ll s=y*3+(z-y)%3; c.a[i][s]++;}}return c;
}
void power(Matrix &ans,Matrix &f,ll b){while(b){if(b&1)ans=ans*f;f=f*f;b>>=1;}return;
}
signed main()
{scanf("%lld%lld%lld",&n,&C,&m);ll z=0;ans.a[0][0]=1;for(ll i=1,k,w;i<=m;i++){scanf("%lld%lld",&k,&w);f=Solve(0);power(ans,f,k-z-1);ans=ans*Solve(w);z=k;}f=Solve(0);power(ans,f,n-z);printf("%lld\n",ans.a[0][0]);return 0;
}