CF107D Crime Management

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

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

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

所以可以进行 $\text{DP}$

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

注意最后答案不是 $f_0$ ,因为有些种类虽然膜 $mul_i$ 不为 $\text{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\n",ans);
    return 0;
}

 

posted @ 2020-11-22 21:46  一叶知秋‘  阅读(96)  评论(0编辑  收藏  举报