• CF107D Crime Management


    我们发现乘积小于等于 $ ext{123}$ ,设 $mul_i=prodlimits_{j=1}^{c}m_j[t_j = i]$ ,就是相当于这一种限制的积,然后我们发现在每一种的个数对 $mul_i$ 取膜的情况下,状态数最多只有 $123$ 种,就可以考虑把状态压缩为一维。

    发现根本不需要搜出所有状态再给编号,考虑一个二维矩阵中的坐标我们是如何把它转成一个数的,这里就相当于多维的坐标,编号就可以变为:

    $((r_1 imes mul_2+r_2) imes mul_3+r_3)...$ (这里 $r_i$ 为对 $mul_i$ 取膜以后的数)

    所以可以进行 $ ext{DP}$

    然而 $n le 10^{18}$ ,发现对于从选 $i$ 个到选 $i+1$ 个,怎么转移与 $i$ 无关,可以在一开始处理出来,所以每一次转移其实本质上都一样,就可以直接套矩阵快速幂了

    注意最后答案不是 $f_0$ ,因为有些种类虽然膜 $mul_i$ 不为 $ ext{0}$ ,但是可能膜某一个 $m_j$ 为 $0$ ,需要枚举每种状态是否可行,然后可行就加上即可

    $code$ :

    #include<cstdio>
    #include<cctype>
    #include<vector>
    
    using namespace std;
    
    #define maxn 33
    #define maxs 222
    #define maxc 1111
    #define mod 12345
    
    inline long long read(){
        long long r=0,f=0;
        char c;
        while(!isdigit(c=getchar()))f|=(c=='-');
        while(isdigit(c))r=(r<<1)+(r<<3)+(c^48),c=getchar();
        return f?-r:r;
    }
    
    inline char get_c(){
        char c;
        while(!isalpha(c=getchar()));
        return c;
    }
    
    int N=1;
    
    long long n;
    
    int cnt,mul[maxn];
    
    long long ans,f[maxs],a[maxs][maxs],c[maxs][maxs];
    
    vector<int> m[maxn];
    
    inline void mulself(){
        for(int i=0;i<N;i++)
            for(int j=0;j<N;j++)
                for(int k=0;k<N;k++)
                    (c[i][j]+=a[i][k]*a[k][j]%mod)%=mod;
        for(int i=0;i<N;i++)
            for(int j=0;j<N;j++){
                a[i][j]=c[i][j];
                c[i][j]=0;
            }
    }
    
    inline void multi(){
        for(int i=0;i<N;i++)
            for(int j=0;j<N;j++)
                (c[0][i]+=f[j]*a[i][j]%mod)%=mod;
        for(int i=0;i<N;i++){
            f[i]=c[0][i];
            c[0][i]=0;
        }
    }
    
    int main(){
        n=read(),cnt=read();
        for(int i=1;i<=cnt;i++){
            int t=get_c()-'A'+1;
            int val=read();
            m[t].push_back(val);
            if(!mul[t])mul[t]=1;
            mul[t]*=val;
        }//没有限制的种类是不能选的,所以mul开始要全置0
        for(int i=1;i<=26;i++)
            if(mul[i])N*=mul[i];
        N++;
        for(int i=0;i<N;i++){//枚举对于每种状态
            int num=i;//选一种以后能转移到哪一个
            long long mult=1;
            for(int j=26;j>=1;j--){
                if(!mul[j])continue;
                int tot=num%mul[j];
                int s=i-tot*mult;
                tot++;
                tot%=mul[j];
                s+=tot*mult;
                num/=mul[j];
                mult*=mul[j];
                a[s][i]++;//存在该转移
            }
        }
        f[0]=1;
        for(;n;n>>=1){
            if(n&1)multi();
            mulself();
        }
        for(int i=0;i<N;i++){//找合法状态
            int num=i;
            bool ok=1;
            for(int j=26;j>=1;j--){
                if(!mul[j])continue;
                int tot=num%mul[j];
                bool b=0;
                for(int k=0;k<(int)m[j].size();k++)
                    b|=((tot%m[j][k])==0);//只要满足一种即可
                ok&=b;
                num/=mul[j];
            }
            if(ok)(ans+=f[i])%=mod;//合法就加上,记得取膜
        }
        printf("%lld
    ",ans);
        return 0;
    }
  • 相关阅读:
    2019 年年终总结
    Java/C# 中为何需要 Getter/Setter?
    使用 supervisord 设置应用开机启动
    Linux 性能监控与故障排查:主要性能指标说明及监控方法
    「Bug」Jenkins Slave 卡顿与僵尸进程
    HTTP/HTTPS 的监听方法,以及浏览器与服务器的协议协商机制
    查询主机公网ip
    Linux Server Swap 分区设置
    Kubernetes 常见错误
    Kubernetes 学习笔记(五):数据卷
  • 原文地址:https://www.cnblogs.com/wyzwyz/p/14021686.html
Copyright © 2020-2023  润新知