Codeforces 433E

/*************************
麻婆豆腐
AC自动机 + 数位dp
好题!

每个字符串都有一个权值,如果一个数字串中出现该串那么
该数字串就的权值就加上字符串的权值,
求区间内数字串权值小于等于给定值的数量。

AC自动机状态转移,数位dp枚举每一位。
再统计符合条件状态数。

**********************/
#include <cstdio>
#include <queue>
#include <cstring>
#include <iostream>
using namespace std;
#define maxn 500010

struct Trie{
    int next[maxn][26],fail[maxn];
    int end[maxn];
    int root,L;

    int newNode(){
        for(int i=0;i<26;i++) next[L][i] = -1;
        end[L++] = 0;
        return L-1;
    }

    void init(){
        L = 0;
        root = newNode();
    }

    void insert(int str[],int len,int val){
        //printf("insert:%s\n",str);
        int now = root;
        for(int i=0;i<len;i++){
            int temp = str[i];
            if(next[now][temp] == -1){
                next[now][temp] = newNode();
            }
            now = next[now][temp];
        }
        end[now] += val;
    }

    void build(){
        queue<int> que;
        fail[root] = root;
        for(int i=0;i<26;i++){
            if( next[root][i] == -1){
                next[root][i] = root;
            }else{
                fail[next[root][i]] = root;
                que.push(next[root][i]);
            }
        }
        while(!que.empty()){
            int now = que.front();
            que.pop();
            end[now] += end[fail[now]];
            for(int i=0;i<26;i++){
                int t = next[now][i];
                if(t==-1){
                    next[now][i] = next[fail[now]][i];
                }else{
                    fail[t] = next[fail[now]][i];
                    //printf("id:%d fail:%d\n",t,fail[t]);
                    que.push(t);
                }
            }
        }
    }

    void debug(){
        for(int i=0;i<L;i++){
            printf("id:%3d fail:%3d end:%3d next:[",i,fail[i],end[i]);
            for(int j=0;j<26;j++){
                printf(" %2d",next[i][j]);
            }
            printf("]\n");
        }
    }
}ac;

typedef long long LL;
const int mod = 1000000007;
int n,m,k;
int len[2];
int p[2][250];
int bit[250];
int buf[250];
LL dp[210][420][520];

LL dfs(int h,int s,int now,bool flag, bool zero){
    now -= ac.end[s];
    if(now < 0) return 0;
    if(h==-1) {
        //cout << sum << " " << now << endl;
        return 1;
    }
    if(!flag && dp[h][s][now] != -1) return dp[h][s][now];

    LL res = 0;
    if(zero){
        res += dfs(h-1,s,now,flag && bit[h]==0,true);
        res %= mod;
    }else{
        res += dfs(h-1,ac.next[s][0],now,flag && bit[h]==0,false);
        res %= mod;
    }

    int up = flag?bit[h]:m-1;
    for(int i=1;i<=up;i++){
        res += dfs(h-1,ac.next[s][i],now,flag && i==up,false);
        res %= mod;
    }

    if(!flag && !zero) dp[h][s][now] = res;
    return res;
}

LL solve(){
    for(int i=0;i<len[0];i++) bit[i] = p[0][i];
    //for(int i=0;i<len[0];i++) cout << bit[i];
    //cout << endl;

    LL ans = -dfs(len[0]-1,0,k,true,true);
    //cout << ans << endl;
    for(int i=0;i<len[1];i++) bit[i] = p[1][i];

    //for(int i=0;i<len[1];i++) cout << bit[i];
    //cout << endl;

    memset(dp,-1,sizeof(dp));

    ans += dfs(len[1]-1,0,k,true,true);
    ans = (ans%mod + mod)%mod;
    return ans;
}

int main(){
    while(cin >> n >> m >> k){
        scanf("%d",&len[0]);
        for(int i=0;i<len[0];i++) scanf("%d",&p[0][len[0] - i - 1]);
        // l-1
        for(int i=0;i<len[0];i++){
            if(p[0][i] == 0) p[0][i] = m-1;
            else{
                p[0][i]--;
                break;
            }
        }
        scanf("%d",&len[1]);
        for(int i=0;i<len[1];i++) scanf("%d",&p[1][len[1] - i - 1]);

        ac.init();
        memset(dp,-1,sizeof(dp));
        int t,temp,v;
        while(n--){
            scanf("%d",&t);
            for(int i=0;i<t;i++) scanf("%d",&buf[i]);
            scanf("%d",&v);
            ac.insert(buf,t,v);
        }
        ac.build();
        //ac.debug();
        cout << solve() << endl;
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值