bzoj 3530: [Sdoi2014]数数

Description

我们称一个正整数N是幸运数,当且仅当它的十进制表示中不包含数字串集合S中任意一个元素作为其子串。例如当S=(22,333,0233)时,233是幸运数,2333、20233、3223不是幸运数。给定N和S,计算不大于N的幸运数个数。

解题报告

简单题,但有点坑,首先如果没有限制就是一个数位DP板子,对于包含的限制,一般是加一维在AC自动机上的节点号,这样就可以保证不存在包含关系了.
设状态 \(f[i][j][k][l]\) 表示前i位数字,目前在AC自动机上的j号节点,选的数字为k,是否严格小于\(0/1\),其实我是个傻逼,写完以后猛地发现 \(k\) 这一维是多余的,而且还需要滚动,但还是讲一下我的做法吧,如果严格小于就可以任意选,前缀都相等,就只能选小于 \(N\) 的这一位的数字,如果走出了包含关系,就不转移.
最坑的地方来了:

10
1
03

这一组数据,如果不判前导0,那么输出\(9\), \(3\) 就被完美的忽略了,所以把 \(k\) 这一位加一个状态,表示前导0然后特殊转移即可

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <queue>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
const int N=20005,mod=1e9+7;
int f[2][N][11][2],fail[N],s[N],n,m;char S[N];
struct node{
   int nxt[11],mark;
}a[N];
void upd(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int root=0,cnt=0;
void add(){
   scanf("%s",S);
   int len=strlen(S),p=0;
   for(int i=0;i<len;i++){
      if(a[p].nxt[S[i]-'0'])p=a[p].nxt[S[i]-'0'];
      else a[p].nxt[S[i]-'0']=++cnt,p=cnt;
   }
   a[p].mark=1;
}
queue<int>q;
void getfail(){
   q.push(root);
   int x,u,v;
   while(!q.empty()){
      x=q.front();q.pop();
      for(int i=0;i<10;i++){
         if(!a[x].nxt[i]){
            a[x].nxt[i]=a[fail[x]].nxt[i];
            continue;
         }
         u=fail[x];
         while(u && !a[u].nxt[i])u=fail[u];
         if(a[u].nxt[i] && a[u].nxt[i]!=a[x].nxt[i])
            fail[a[x].nxt[i]]=a[u].nxt[i];
         v=a[x].nxt[i];a[v].mark|=a[fail[v]].mark;q.push(v);
      }
   }
}
void work()
{
   scanf("%s%d",S+1,&m);n=strlen(S+1);
   for(int i=1;i<=n;i++)s[i]=S[i]-'0';
   for(int i=1;i<=m;i++)add();
   getfail();
   int x,to,b;bool t=0,tt=1;
   for(int i=1;i<=s[1];i++){
      x=a[root].nxt[i];
      if(a[x].mark)continue;
      f[t][x][i][i<s[1]]=1;
   }
   f[t][0][10][1]=1;
   for(int i=1;i<n;i++){
      for(int j=0;j<=cnt;j++){
         for(int k=0;k<=9;k++){
            x=f[t][j][k][0];f[t][j][k][0]=0;
            if(x){
               for(int l=0;l<=s[i+1];l++){
                  to=a[j].nxt[l];if(a[to].mark)continue;
                  b=(l<s[i+1]);
                  upd(f[tt][to][l][b],x);
               }
            }
            x=f[t][j][k][1];f[t][j][k][1]=0;
            if(x){
               for(int l=0;l<10;l++){
                  to=a[j].nxt[l];if(a[to].mark)continue;
                  upd(f[tt][to][l][1],x);
               }
            }
         }
         x=f[t][j][10][1];f[t][j][10][1]=0;
         for(int l=1;l<=10;l++){
            to=a[j].nxt[l];if(a[to].mark)continue;
            upd(f[tt][to][l][1],x);
         }
      }
      t^=1;tt^=1;
   }
   int ans=0;
   for(int i=0;i<=cnt;i++)
      for(int k=0;k<=10;k++)
         for(int l=0;l<=1;l++)
            upd(ans,f[t][i][k][l]);
   ans=(ans-1+mod)%mod;
   printf("%d\n",ans);
}
int main(){work();return 0;}

posted @ 2017-10-08 21:33  PIPIBoss  阅读(157)  评论(0编辑  收藏  举报