• LOJ #3098. 「SNOI2019」纸牌 动态规划+矩阵乘法


    题意:有 $n$ 种牌,每种牌有 $C$ 张.

    有两种方法能组成一叠:

    - $(i,i+1,i+2)$
    - $(i,i,i)$

    一副牌是合法的,当且仅当这副牌能被分成若干叠.

    给出牌的种类数 $n$ 以及每种的张数 $C$,和每种牌必选的个数.(如果该牌必选 $k$ 张,则有 $C-k$ 张是可选可不选的)

    数据范围:$nleqslant 10^{18},Cleqslant 10^3$

    如何判定一副牌合不合法:一定把 $(i,i,i)$ 这种类型的都打掉,然后再判断 $(i,i+1,i+2)$ 合不合法.

    至于证明,我们容易证明如果只有 3 张牌 1,2,3 的话显然要满足:

    $(a-k)equiv 0(mod 3)$

    $(b-k) equiv 0(mod 3)$

    $(c-k) equiv 0(mod 3)$

    有:$a,b,c equiv k(mod 3)$ ,强制让 $kleqslant 2$ 就行.

    多个的话由 3 个去推广即可.

    由此,我们就设 $f_{i,j,k}$ 表示当前为第 $i$ 种牌,凑了 $j$ 对 $(i-1,i)$,$k$ 对 $i$ 的方案数.

    那么有转移:$f_{i,j,k}=f_{i-1,l,j} imes (frac{C-j-l-k}{3}+1)$

    除法是向下取整,然后 +1 是因为可以不选 $(i,i,i)$ 这种的.

    这个转移在 $j+l+k >= need$ 的时候是显然正确的,但是当 $j+l+k<need$ 的时候要强制再选一些.

    转移也是十分简单的,算一下需要最少再选几个 $(i,i,i)$ (注意这个要向上取整,宁可多选)

    code:  

    #include <bits/stdc++.h>      
    #define N 100007 
    #define ll long long 
    #define mod 998244353 
    #define setIO(s) freopen(s".in","r",stdin) 
    using namespace std;    
    ll n,k[N];    
    int C,X,a[N];     
    int add(int x,int y) { return (ll)((ll)x+y+mod)%mod; }    
    int dec(int x,int y) { return (ll)(x-y+mod)%mod; }     
    int mul(int x,int y) { return (ll)x*y%mod; }        
    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;   
        }   
        int *operator[](int x) { return m[x]; }  
        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[j*3+k][i*3+j]=(C-i-j-k)/3+1;        
    }
    int main() 
    { 
        // setIO("input");     
        scanf("%lld%d%d",&n,&C,&X),init();   
        for(int i=1;i<=X;++i)   
            scanf("%lld%d",&k[i],&a[i]); 
        A[0][0]=1;    
        for(int i=1;i<=X;++i)
        {
            A=A*(B^(k[i]-k[i-1]-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,con=(now>a[i])?now:(a[i]+((now-a[i])%3+3)%3);     
                        if(con<=C)   
                            tmp[j*3+k][l*3+j]=(C-con)/3+1;   
                    }
            A=A*tmp;     
        }
        A=A*(B^(n-k[X]));      
        printf("%d
    ",A[0][0]);  
        return 0; 
    }
    

      

  • 相关阅读:
    How to change the property of a control from a flowlayoutpanel?
    Add controls dynamically in flowlayoutpanel
    CLR via C# 读书笔记 6-2 不同AppDomain之间的通信 z
    应用程序域 z
    C# 在SQLite数据库中存储图像 z
    Using .NET 4's Lazy<T> 实现单实例
    tpl demo
    SQLite批量插入,修改数据库 zt
    Dev表格导出工具类 z
    easyui 设置一加载,搜索框立即弹出的效果
  • 原文地址:https://www.cnblogs.com/guangheli/p/12492549.html
Copyright © 2020-2023  润新知