zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    土法炼钢:怎么实现一个简单的B+Tree In-Disk
    panguan(判官):一个自研的任务执行引擎的工程实践
    团队源代码管理
    测试——俄罗斯方块
    测试——连连看
    测试——食物链教学小工具
    测试——约跑APP
    beta-review阶段贡献分分配
    第十周PSP&进度条
    吉林市一日游
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/15348598.html
Copyright © 2011-2022 走看看