省选模拟题 T1 A

模拟题 T1 A

Description
现在有无数个HJA,编号从1一直到无穷。对于编号为 x 的 HJA,我们考虑如何计算他的价值。我们将x写作 m 制的数,然后考虑我们给出的另外n m 进制下的数(这些数可能有前导零),每个数的权值为vi。如果第 i 个数在x中出现了 r 次(在匹配的时候 HJA是没有前导零的,但这些数的前导零也必须被匹配),那么我们便把x的价值加上 r×vi ,所以编号为 x 的HJA的权值应该是ki=1ri×vi。现在HJA 希望知道 [l,r] 中有多少个 HJA 的价值是不超过 k 的。
Input
第一行是三个正整数n,m,k
第二行第一个整数 lenl 代表 l 的位数, 接下来lenl个整数代表 l m进制下的每一位。
第三行第一个整数 lenr 代表 r 的位数,接下来lenr个整数代表 r m进制下的每一位。
接下来 n 行, 每行第一个数代表第i个数的长度,接下来的读入方式同 l,r 。最后有一个数代表这个数的权值。
Output
输出答案模 109+7
Sample Input 1
2 10 1
1 1
3 1 0 0
1 1 0
1 0 1
Sample Output 1
97

Sample Input 2
2 10 12
2 5 9
6 6 3 5 4 9 7
2 0 6 1
3 6 7 2 1
Sample Output 2
635439

Sample Input 3
4 2 6
6 1 0 1 1 1 0
6 1 1 0 1 0 0
1 1 2
3 0 1 0 5
4 0 1 1 0 4
3 1 0 1 2
Sample Output 3
2

Solution
该题是一个比较裸的动态规划题,思维难度并不高。考虑 n 个数与一个数的匹配过程,实际上就是字符串问题中的多串匹配问题,所以我们对这n个串建立AC自动机。那么对于一个特定的数,我们只要在该AC自动机上走一遍就能知道 n 个数的每一个数分别出现了多少次,从而算出这个数的权值。
考虑另外一部分,我们要求[l,r]之间满足条件的数的个数,等价于求 [1,r] [1,l1] 内满足条件的数的个数,然后相减即可。这样问题就变成了一个经典的数位DP问题,记 f[i][j][k][l] 表示我们现在已经填好了这个数的前 i 位同时走到了AC自动机的第k个状态,当前的价值是 k 的方案数。l 0/1 变量,表示当前数是等于上界值还是小于上界值,转移的方法枚举下一位所填的数即可。

Program

#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std ;
const int N=210;
const int M=25;
const int K=510;
const int mod=1e9+7;
#define newnode ++tlen
#define inc(a,b) {a+=b; if (a>=mod) a-=mod;}

int f[N][N][K][2];//DP数组
int q[N];
int n,m,k;
int word[N][N],wlen[N];//存储有权单词及其长度
int tlen,root;//trie树的节点个数,trie树根节点编号
int le[N],ri[N],llen,rlen;//左边界和右边界
typedef struct Trie{
    int next[M];
    int value,fail;
    Trie (){
        memset (next,0,sizeof(next));
        value=fail=0;
    }
}T;
T trie[N];
void T_insert (int *s,int l,int v){
    int p=root,i;
    for (i=1;i<=l;i++){
        if (!trie[p].next[s[i]]) trie[p].next[s[i]]=newnode;
        p=trie[p].next[s[i]];
    }
    trie[p].value+=v;
}
void init (){
    scanf ("%d %d %d",&n,&m,&k);
    int i,j,v;
    root=newnode;
    scanf ("%d",&llen);
    for (i=llen;i>=1;i--) scanf ("%d",&le[i]);
    scanf ("%d",&rlen);
    for (i=rlen;i>=1;i--) scanf ("%d",&ri[i]);
    for (i=1;i<=n;i++){
        scanf ("%d ",&wlen[i]);
        for (j=wlen[i];j>=1;j--){
            scanf ("%d ",&word[i][j]);
        }
        scanf ("%d",&v);
        T_insert(word[i],wlen[i],v);
    }
}
void build_ACaotomaton (){
    int front=1,tail=1;
    int now,i,p;
    q[1]=root;
    while (front<=tail){
        now=q[front++];
        for (i=0;i<m;i++){
            if (trie[now].next[i]){
                p=trie[now].fail;
                while (p){
                    if (trie[p].next[i]){
                        trie[trie[now].next[i]].fail=trie[p].next[i];
                        break;
                    }
                    p=trie[p].fail;
                }
                if (!p) trie[trie[now].next[i]].fail=root;
                trie[trie[now].next[i]].value+=trie[trie[trie[now].next[i]].fail].value;
                q[++tail]=trie[now].next[i];
            }
            else {
                trie[now].next[i]=trie[trie[now].fail].next[i];
                if (!trie[now].next[i]) trie[now].next[i]=root;
            }
        }
    }
}
int solve(int *y,int l){
    if (!l) return 0;
    int ans=0;
    int a,b,c,d,e;
    int p,data;
    for (a=1;a<l;a++)
        for (b=1;b<=tlen;b++)
            for (c=0;c<=k;c++)
                inc(ans,f[a][b][c][0]);
    for (a=l;a>=1;a--){
        for (b=1;b<=tlen;b++){
            for (c=(a==l);c+(a!=1)<=y[a];c++){
                p=b;
                data=0;
                p=trie[p].next[c];
                data+=trie[p].value;
                for (d=a+1;d<=l;d++){
                    p=trie[p].next[y[d]];
                    data+=trie[p].value;
                }
                for (d=0;d<=k-data;d++){
                    for (e=0;e<=1;e++) inc(ans,f[a-1][b][d][e]);
                }
            }
        }
    }
    return ans;
}
int main (){
    init ();
    build_ACaotomaton ();
    n=max (llen,rlen);
    f[0][root][0][0]=1;
    int a,b,c,d,e,i;
    for (a=0;a<n;a++)
        for (b=1;b<=tlen;b++)
            for (c=0;c<=k;c++)
                for (d=0;d<=1;d++)
                    if (f[a][b][c][d])
                        for (e=0;e<m;e++)
                            if (c+trie[trie[b].next[e]].value<=k)
                                inc(f[a+1][trie[b].next[e]][c+trie[trie[b].next[e]].value][e==0],f[a][b][c][d]);
    le[1]--;
    for (i=1;i<=llen;i++){
        if (le[i]<0) {
            le[i]+=m;
            le[i+1]--;
        }
        else break;
    }
    if (!le[llen]) llen--;
    printf("%d\n",(solve(ri,rlen)-solve(le,llen)+mod)%mod);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值