51nod 1583 犯罪计划 dp+矩阵乘法+hash

题意

文泽想在埃及做案n次,并且想在最后不用得到惩罚。案件的被分成几种类型。比如说,案件A,当案件A被重复犯两次时,案件A将被认为不是犯罪案件,因此犯案人不用得到惩罚。也就是说,案件A被犯偶数次时,犯案人将不用得到惩罚。又比如案件B,当案件B被犯的次数是5的倍数时,犯案人将不用得到惩罚。
更具体的说,现在知道有c组条件。每组条件包含的信息如下:
1. 案件类型 ti ,
2. 底数 mi ,表示该类型案件重复n* mi (n是非负整数)次时,犯案人可以不用得到惩罚。
对于同一种案件,他的条件可能会被列出多个,在这种情况下,犯案人对该种案件犯罪次数至少满足其中一个条件就可以不受到惩罚。
文泽想要知道他犯n次罪后,并且不得到惩罚的犯罪情况的种数。
不得到惩罚的条件是,对于所有犯过n个案件的所有案件类型中,每种类型的案件都不用得到惩罚。
每个案件的犯罪顺序是有意义的。说明一下,现在有两种犯罪的案件序列W1和W2,如果对于所有的i(1≤i≤n)使得 W1i=W2i ,我们就认为这两个案件序列是一样的。
0≤n≤10^18,0≤c≤1000,所有mi的乘积不会超过123。

分析

细节题。。。
注意到所有mi的乘积不会超过123,那么我们可以用m个数表示一个状态:第i个数a[i]表示条件i所对应的数的出现次数模mi的值。显然状态数不会超过123。
记录状态的话可以把状态用hash压成一个数扔到map里面。
然后直接矩阵快速幂优化即可。

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<map>
using namespace std;

typedef long long LL;
typedef unsigned long long ull;

const int inf=1000000000;
const int MOD=12345;

LL n;
int m,tot,sz;
pair<int,int> con[1005];
struct data{int a[10];}bel[150];
map<ull,int> num;

struct Matrix
{
    int a[150][150];

    void clear(int n)
    {
        for (int i=1;i<=n;i++)
            for (int j=1;j<=n;j++)
                a[i][j]=0;
    }

    void unit(int n)
    {
        clear(n);
        for (int i=1;i<=n;i++) a[i][i]=1;
    }
}a;

void init()
{
    scanf("%lld%d",&n,&m);
    for (int i=1;i<=m;i++)
    {
        char ch[2];int x;
        scanf("%s%d",ch,&x);
        con[i]=make_pair((int)(ch[0]-'A'),x);
    }
    sort(con+1,con+m+1);
    int tmp=m;
    int vis[26];
    memset(vis,0,sizeof(vis));
    for (int i=1;i<=m;i++)
    {
        if (!vis[con[i].first]) vis[con[i].first]=++tot;
        con[i].first=vis[con[i].first];
    }
    for (int i=1;i<=m;i++)
        if (con[i].second==1&&con[i].first!=inf)
        {
            int j=i+1;
            while (j<=m&&con[j].first==con[i].first) con[j].first=inf,tmp--,j++;
            con[i].first=inf;tmp--;
        }
    sort(con+1,con+m+1);
    m=tmp;
}

ull get_hash(data u)
{
    ull w=1;
    for (int i=1;i<=m;i++) w=w*233333+u.a[i];
    return w;
}


void dfs(data u,int x)
{
    if (x>m) {num[get_hash(u)]=++sz;bel[sz]=u;return;}
    dfs(u,x+1);
    while (u.a[x]+1<con[x].second) u.a[x]++,dfs(u,x+1);
}

void build()
{
    data u;
    memset(u.a,0,sizeof(u.a));
    dfs(u,1);
    for (int i=1;i<=sz;i++)
        for (int j=1;j<=tot;j++)
        {
            u=bel[i];
            for (int k=1;k<=m;k++) if (con[k].first==j) (u.a[k]+=1)%=con[k].second;
            a.a[i][num[get_hash(u)]]++;
        }
}

bool check(int x)
{
    data u=bel[x];
    for (int i=1;i<=m;i++)
    {
        int j=i;
        while (j<m&&con[j+1].first==con[j].first) j++;
        int flag=0;
        for (int k=i;k<=j;k++) if (!u.a[k]) {flag=1;break;}
        if (!flag) return 0;
        i=j;
    }
    return 1;
}

void mul(Matrix &c,Matrix a,Matrix b)
{
    c.clear(sz);
    for (int i=1;i<=sz;i++)
        for (int k=1;k<=sz;k++)
            for (int j=1;j<=sz;j++)
                (c.a[i][j]+=a.a[i][k]*b.a[k][j])%=MOD;
}

Matrix ksm(Matrix x,LL y)
{
    Matrix ans;ans.unit(sz);
    while (y)
    {
        if (y&1) mul(ans,ans,x);
        mul(x,x,x);y>>=1;
    }
    return ans;
}

int main()
{
    init();
    build();
    a=ksm(a,n);
    int ans=0;
    for (int i=1;i<=sz;i++) if (check(i)) ans+=a.a[1][i],ans-=ans>=MOD?MOD:0;
    printf("%d",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值