Crime Management CodeForces - 107D

Crime Management CodeForces - 107D
问题描述: Zeyad 想要在埃及犯下N项罪行并且不想受到惩罚。有若干种类的罪行。例如,行贿是一项罪行但是当行贿重复两次的时候就不被认为是犯罪。因此,行贿在犯偶数次的时候不被当做犯罪。超速是一项罪行,但是当重复的次数是五的倍数的时候就不被认为是犯罪。
  更特别的,现在已知有C个犯罪的条件。每个条件描述罪行的种类Ti和倍数Mi。如果Zeyad的犯Ti这罪名的次数是Mi的倍数,那么Zeyad就不会因此受到惩罚。有些罪名可能在条件中出现多次。那么只要满足至少一个条件Zeyad就不会受罚。当然如果Zeyad犯某种罪行的次数为0,他自然由于遵守法律而不会受罚。
  现在Zeyad想知道他有多少种犯罪方式使得他恰好犯下n次罪却不会受到任何惩罚。
  犯罪的顺序是有关系的。更正式的说,两个犯罪序列W1与W2被认为相同,当且仅当对于所有 1 ≤ i ≤ n,w1i = w2i成立。

输入格式:第一行有两个整数n,c 分别表示Zeyad想要犯罪的次数和他所知的条件的数目。紧接着是c个条件。共有26种罪名,分别用A-Z表示。每个条件包含一个大写字母表示罪名的类型和一个正整数表示倍数。所有条件中倍数的乘积不超过123。某些条件可以出现多次。当倍数是1时表示无论犯罪多少次都不会受到惩罚。显而易见,对于那些没有在条件中列出的罪名Zeyad不会考虑去犯它们因为这会不可避免地受到惩罚。

输出格式:输出一个非负整数,表示Zeyad恰好犯下n次罪却不会受到任何惩罚的犯罪方式数目模12345的值。

数据规模和约定: 0 ≤ n ≤ 1018, 0 ≤ c ≤ 1000

题解:此题最重要的一点是π(d)<=123。令p[i]为字母i所有d的乘积。p[i]<=123
f[i][a][b]…[z]:表示长度为i的字符串,‘A’个数 mod p[1] 等于a,‘B’个数 mod p[2] 等于b ……‘Z’个数 mod p[26] 等于z的情况总数。

因为π(d)<=123,所以每个n最多有123种状态。那么,把状态全部爆搜出来之后编号,就可以用一个123*123的矩阵来表示状态的转移。答案为矩阵的n次方。

坑点:n=0时一定有一种情况。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<map>
#include<vector>
#define pb push_back
using namespace std;
typedef long long ll;
const int mod=12345;
const int N=1000+5;
const int L=123;
template <class T>
inline void getin(T&num){
    char c;bool flag=0;num=0;
    while((c=getchar())<'0'||c>'9')if(c=='-')flag=1;
    while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
    if(flag) num=-num;
    //cin>>num;
}
ll n;
map<vector<int>,int> num;
vector<int> letter,let[L+5],mo[30];
int m,x,d,cnt=1,p[30],vis[30];
char str[2];bool zero[30];
inline void add(ll&a,ll b){a+=b;if(a>=mod)a-=mod;}
struct matrix{
    ll mat[L+5][L+5];
    inline void Init(){memset(mat,0,sizeof mat);}
    inline void One(){
        memset(mat,0,sizeof mat);
        for(int i=1;i<=cnt;i++) mat[i][i]=1;
    }
    matrix operator * (const matrix&a)const{
        matrix ret;ret.Init();
        for(int i=1;i<=cnt;i++)
            for(int j=1;j<=cnt;j++)
                for(int k=1;k<=cnt;k++)
                    add(ret.mat[i][j],mat[i][k]*a.mat[k][j]%mod);
        return ret;
    }
    matrix operator *= (const matrix&a){
        return (*this)=(*this)*a;
    }
    matrix operator ^ (const ll b)const{
        matrix ret,tep;ll p=b;
        ret.One(),tep=(*this);
        while(p){
            if(p&1) ret*=tep;
            tep*=tep,p>>=1;
        }
        return ret;
    }
    matrix operator ^= (const ll b){
        return (*this)=(*this)^b;
    }
}statu;
void dfs(int x){
    if(x==27){
        //printf("%d\n",cnt);
        num[letter]=++cnt;
        for(int i=0;i<=26;i++)
            let[cnt].pb(letter[i]);
        return ;
    }
    if(!p[x]) return dfs(x+1);
    for(int i=0;i<p[x];i++)
        letter[x]=i,dfs(x+1);
}
void build(int x){
    if(x==27){
        int id=num[letter];
        vector<int> tep;
        for(int i=0;i<=26;i++)
            tep.pb(letter[i]);
        for(int i=1;i<=26;i++){ 
            if(!p[i]) continue ;
            int to=(tep[i]+1)%p[i];
            swap(tep[i],to);
            add(statu.mat[id][num[tep]],1);
            swap(tep[i],to);
        }
        return ;
    }
    if(!p[x]) return build(x+1);
    for(int i=0;i<p[x];i++)
        letter[x]=i,build(x+1);
}
inline bool check(int p){
    for(int i=1;i<=26;i++){
        int siz=mo[i].size();
        if(!siz){
            if(!let[p][i]) continue ;
            else return 0;
        }
        bool flag=0;
        for(int j=0;j<siz;j++){
            if(!mo[i][j]){
                if(!let[p][i]){flag=1;break ;}
                continue ;
            }
            if(let[p][i]%mo[i][j]==0){
                flag=1;break ;
            }
        }
        if(!flag) return 0;
    }
    return 1;
}
int main(){
    getin(n),getin(m);
    if(!n) puts("1"),exit(0);//坑
    if(!m) puts("0"),exit(0);
    letter.pb(0);
    for(int i=1;i<=26;i++)
        p[i]=1,letter.pb(0);
    for(int i=1;i<=m;i++){
        scanf("%s%d",str,&d);
        if(!d) zero[str[0]-'A'+1]=1;
        else p[str[0]-'A'+1]*=d;
        mo[str[0]-'A'+1].pb(d);
        vis[str[0]-'A'+1]++;
    }
    for(int i=1;i<=26;i++)
        if(zero[i]&&vis[i]==1) p[i]=0;
    for(int i=1;i<=26;i++)
        if(!vis[i]) p[i]=0;
    dfs(1);
    for(int i=1;i<=26;i++) letter[i]=0;
    build(1);
    for(int i=1;i<=26;i++) letter[i]=0;
    for(int i=1;i<=26;i++){
        if(!p[i]) continue ;
        letter[i]=1%p[i];
        if(num.count(letter))
            add(statu.mat[1][num[letter]],1);
        letter[i]=0;
    }
    for(int i=1;i<=26;i++) letter[i]=0;
    statu^=n;
    ll ans=0;
    for(int i=2;i<=cnt;i++)
        if(check(i))
            add(ans,statu.mat[1][i]);
    cout<<ans<<'\n';
    return 0;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值