ZOJ 3494

超级神奇有趣题。

AC自动机+数位DP。其实,数位DP在处理含有某些数字时特别不好处理,应该把它倒转为求不含有。这道题把数位DP+AC自动机结合起来,实在是很巧妙,把数字变为串来处理,强大!

要使用AC自动机来处理数位DP,首先就是要确定哪些状态由当前状态开始是不可以到达的,有哪些是可以到达的。这是显而易见的,因为既然是数位DP,必定涉及到数字的问题,每改变一个数字就会到达自动机上的一个状态,确定哪些状态可达或不可达是很有必要的。这就要求要构建trie图了。

其次就是DFS了,在进行DFS深搜前,要确定当前状态变更一个数字后的状态是否可达,再进行转移。

在这题,要处理前导0的问题,因为0000(十进制)里,其实只代表一个0,但可能会出现禁止状态(如二进制连续五个0违法,但四个0不违法)。

  1 #include <iostream>
  2 #include <cstdio>
  3 #include <cstring>
  4 #include <algorithm>
  5 #define LL long long
  6 using namespace std;
  7 const LL MOD=1000000009;
  8 const int root=0;
  9 int trie[2010][2],tot,bit[210];
 10 int next[2010][10],fail[2010];
 11 bool tag[2010];
 12 char str[210];
 13 LL dp[210][2010];
 14 int que[2010],head,tail;
 15 
 16 void clr(int now){
 17     trie[now][0]=trie[now][1]=-1;
 18 }
 19 
 20 void insert(){
 21     int p=root,i=0,len=strlen(str);
 22     while(i++<len){
 23         if(trie[p][str[i-1]-'0']==-1){
 24             trie[p][str[i-1]-'0']=++tot;
 25             clr(tot);
 26         }
 27         p=trie[p][str[i-1]-'0'];
 28     }
 29     tag[p]=true;
 30 }
 31 
 32 void build_ac(){
 33     head=tail=0;
 34     que[tail++]=root;
 35     while(head!=tail){
 36         int tmp=que[head++];
 37         int p=-1;
 38         for(int i=0;i<2;i++){
 39             if(trie[tmp][i]!=-1){
 40                 if(tmp==root) fail[trie[tmp][i]]=root;
 41                 else{
 42                     p=fail[tmp];
 43                     while(p!=-1){
 44                         if(trie[p][i]!=-1){
 45                             fail[trie[tmp][i]]=trie[p][i];
 46                             break;
 47                         }
 48                         p=fail[p];
 49                     }
 50                     if(p==-1) fail[trie[tmp][i]]=root;
 51                 }
 52                 if(tag[fail[trie[tmp][i]]]) tag[trie[tmp][i]]=tag[fail[trie[tmp][i]]];
 53                 que[tail++]=trie[tmp][i];
 54             }
 55             else{  
 56                 if(tmp==root) trie[tmp][i]=root;
 57                 else{
 58                     p=fail[tmp];
 59                     while(p!=-1){
 60                         if(trie[p][i]!=-1){
 61                             trie[tmp][i]=trie[p][i];
 62                             break;
 63                         }
 64                         p=fail[p];
 65                     }
 66                     if(p==-1) trie[tmp][i]=root;
 67                 }
 68             }
 69         }
 70     }
 71 }
 72 
 73 void interval(){
 74     int len=strlen(str)-1;
 75     char tmp;
 76     for(int i=len;i>=len-i;i--){
 77         tmp=str[i];
 78         str[i]=str[len-i];
 79         str[len-i]=tmp;
 80     }
 81 }
 82 
 83 int cal_next(int p,int j){
 84     if(tag[p]) return -1;
 85     for(int k=3;k>=0;k--){
 86         p=trie[p][(j>>k)&1];
 87         if(tag[p]) return -1;
 88     }
 89     return p;
 90 }
 91 
 92 void Calculation(){
 93     for(int i=0;i<=tot;i++){
 94         for(int j=0;j<=9;j++){
 95             next[i][j]=cal_next(i,j);
 96         }
 97     }
 98 }
 99 
100 LL dfs(int len,int j,bool limit,bool z){
101     if(len==-1) return 1LL;
102     if(!limit&&dp[len][j]!=-1) return dp[len][j];
103     int up=limit?bit[len]:9;
104     LL ans=0;
105     if(z&&len>0){
106         ans+=dfs(len-1,j,limit&&up==0,true);
107     }
108     else{
109         if(next[j][0]!=-1&&!tag[next[j][0]])
110         ans+=dfs(len-1,next[j][0],limit&&up==0,false);
111     }
112     for(int i=1;i<=up;i++){
113         if(next[j][i]!=-1&&!tag[next[j][i]]){
114             ans+=dfs(len-1,next[j][i],limit&&i==up,false);
115         }
116     }
117     ans%=MOD;
118     if(!limit) dp[len][j]=ans;
119     return ans;
120 }
121 
122 LL slove(){
123     int len=strlen(str);
124     len--;
125     for(int i=len;i>=0;i--)
126     bit[i]=str[i]-'0';
127     return dfs(len,0,true,true);
128 }
129 
130 int main(){
131     int T,n,len;
132     scanf("%d",&T);
133     while(T--){
134         memset(tag,false,sizeof(tag));
135         memset(dp,-1,sizeof(dp));
136         memset(fail,-1,sizeof(fail));
137         scanf("%d",&n);
138         tot=0;
139         clr(root);
140         for(int i=0;i<n;i++){
141             scanf("%s",str);
142     //        cout<<str<<endl;
143             insert();
144         }
145         build_ac();
146         Calculation();
147         scanf("%s",str);
148     //    cout<<str<<endl;
149         len=strlen(str);
150         interval();
151     //    cout<<str<<endl;
152         for(int i=0;i<len;i++){
153             if(str[i]=='0')
154             str[i]='9';
155             else {
156                 str[i]--;
157                 break;
158             }
159         }
160         if(str[len-1]=='0'&&len!=1) str[len-1]='\0';
161         LL ans1=slove();
162         scanf("%s",str);
163         interval();
164 //        memset(dp,-1,sizeof(dp));
165         LL ans2=slove();
166         printf("%lld\n",((ans2-ans1)%MOD+MOD)%MOD);
167     }
168     return 0;
169 }
View Code

 

转载于:https://www.cnblogs.com/jie-dcai/p/4338329.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值