• P5371-[SNOI2019]纸牌【矩阵乘法】


    正题

    题目链接:https://www.luogu.com.cn/problem/P5371


    题目大意

    (n)种牌,每种牌最多(C)张,(X)个限制形如(k_i)种牌至少(a_i)张。

    求所有牌的序号能分成((i,i,i))或者((i,i+1,i+2))的若干组的方案数。

    (1leq nleq 10^{18},0leq X,Cleq 1000)


    解题思路

    看到这个(n)的范围考虑矩阵乘法,然后考虑上面那个叠的东西,因为((i,i,i))能构成一叠,所以三个((i,i+1,i+2))可以分成三个((i,i,i)),所以这样的话不难发现一个牌最多有(6)张由前或后构成一叠,再进一步的,设(f_{i,j})表示上个选了(i)张,这一个选了(i+j)张,此时有(i,jleq 3),这样状态数就是(9)了,然后暴力矩阵乘法,局部暴力即可。
    时间复杂度:(O(X9^3log n+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
    ",ans.a[0][0]);
    	return 0;
    }
    
  • 相关阅读:
    常见时间复杂度及对应关系
    【转载】Locust实现集合点
    curl 查看接口的网络分段响应时间
    python-字符串操作分类小结
    Jquery--1--选择器分类
    【转】linux shell编程实例总结
    随笔分类整理
    linux备忘录-日志档案
    linux备忘录-系统服务daemon
    linux备忘录-程序管理与SELinux
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/15348598.html
Copyright © 2020-2023  润新知