• [SNOI2019]纸牌


    传送门

    Description

    有一副纸牌。牌一共有(n)种,分别标有 (1,2,...,n),每种有(C)张。故这副牌共有(nC)张。

    三张连号的牌((i,i+1,i+2))或三张相同的牌 ((i,i,i)) 可以组成一叠。如果一组牌可以分成若干(包括零)叠,就称其为一组王牌

    你从牌堆中摸了一些初始牌。现在你想再挑出一些牌组成一组王牌,请问有多少种可能组成的王牌呢?答案对(998244353)取模。

    两组牌相同当且仅当它们含有的每一种牌数量都相同。

    Solution

    发现这道题中的(n)很大,所以考虑矩阵乘法

    发现((i,i+1,i+2))这样的牌,对于相同的(i),最多只有两叠

    所以考虑一个(3*3)的状态表示当前数(i)(i-1)开始的顺子的数量((0,1,2))

    考虑如何转化到下一个数,发现需要枚举(i+1)开始的顺子数,将其乘上可行的((i+1,i+1,i+1))的数量的个数

    将原题的(dp)转化位矩阵乘法

    对于已经有初始牌的数,我们考虑单独拿出来转移

    可以先把转移矩阵的次幂算出来,减小常数


    Code

    #include<bits/stdc++.h>
    #define ll long long
    using namespace std;
    #define reg register
    #define int ll
    inline ll read()
    {
    	ll x=0,f=1;char ch=getchar();
    	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    	while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    	return x*f;
    }
    const int P=998244353,MX=1005;
    ll n,C,X,A[MX],K[MX];
    int Mul(int x,int y){return (1LL*x*y)%P;}
    int Add(int x,int y){return (x+y)%P;}
    struct Matrix
    {
    	int a[9][9];
    	Matrix(){memset(a,0,sizeof a);}
    	void emp(){for(int i=0;i<9;++i)a[i][i]=1;}
    	Matrix operator *(Matrix b)
    	{
    		Matrix c;
    		register int i,j,k;
    		for(k=0;k<9;++k)for(i=0;i<9;++i)for(j=0;j<9;++j)
    		c.a[i][j]=Add(c.a[i][j],Mul(b.a[i][k],a[k][j]));
    		return c;
    	}
    }tmp,opt[70],ans;
    Matrix OPT(int m)
    {
    	Matrix r;int x=0;r.emp();
    	for(;m;m>>=1,++x)if(m&1)r=r*opt[x];
    	return r;
    }
    signed main()
    {
    	n=read();C=read();X=read();
    	register int i,j,k,l;
    	for(i=1;i<=X;++i) K[i]=read(),A[i]=read();
    	for(i=0;i<3;++i)for(j=0;j<3;++j)for(k=0;k<3;++k)
    		if(i+j+k<=C)opt[0].a[j*3+k][i*3+j]=1+(C-i-j-k)/3;
    	for(i=1;i<70;++i) opt[i]=opt[i-1]*opt[i-1];
    	ans.a[0][0]=1;
    	for(l=1;l<=X;++l)
    	{
    		ans=ans*OPT(K[l]-K[l-1]-1);
    		memset(tmp.a,0,sizeof tmp.a);
    		for(i=0;i<3;++i)for(j=0;j<3;++j)for(k=0;k<3;++k)
    		{
    			int least=i+j+k;
    	        if(least<A[l]) least=A[l]+((least-A[l])%3+3)%3;
                if(least<=C) tmp.a[j*3+k][i*3+j]=1+(C-least)/3;
    		}
    		ans=ans*tmp;
    	}
    	ans=ans*OPT(n-K[X]);
    	printf("%lld
    ",ans.a[0][0]);
    	return 0;
    }
    


    Blog来自PaperCloud,未经允许,请勿转载,TKS!

  • 相关阅读:
    判别模型、生成模型与朴素贝叶斯方法
    git的安装已经连github
    uva 10061 How many zero's and how many digits ?
    Java菜鸟学习笔记()--面向对象篇(七):Wrapper Class包装类
    丁香园技术负责人冯大辉近日在知乎上披露了当年共同创办阿里巴巴的18个合伙人的近况:
    不用派生CTreeCtrl不用繁琐的过程 教你如何让CTreeCtrl的每一项有ToolTip提示
    数据结构排序系列详解之三 冒泡排序
    HDU 4612 (13年多校第二场1002)无向图缩点,有重边
    Mac下cocos2dx3.1用Cocos IDE写的Lua binding篇01
    SECURITY_ATTRIBUTES 设置低权限
  • 原文地址:https://www.cnblogs.com/PaperCloud/p/11221366.html
Copyright © 2020-2023  润新知