您的位置:首页 > 其它

bzoj 3530 : [Sdoi2014]数数

2017-02-23 22:47 453 查看
  一眼AC自动机上DP,f[i][j][0]到第j个节点表示前i位与n一样的方案,1表示不一样的方案。

  事实证明我每回写数位dp都要wa到神志模糊,数据一有0就跪,所以加了各种特判。。。

  因为如果数据中有0032这种的话直接dp会把32判为不合法,而事实上它是合法的,因为要去掉前缀0.

  所以每回从根向0的边转移时要少转移1,相当于把0000....这个全为0的前缀保留在根,为了保留这个前缀还要从第二次开始每次f[i][0][1]++。

  好像每次写数位dp都是加了一堆特判才过的。。。

  

#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define p 1000000007
using namespace std;
int m,n;
char s[2000];
int ch[2000][10];int cnt;
bool v[2000];
bool flag;
void build()
{
int len=strlen(s);
int now=0;
for(int i=0;i<len;i++)
{
int c=s[i]-'0';
if(ch[now][c])now=ch[now][c];
else
{
ch[now][c]=++cnt;
now=ch[now][c];
}
}
v[now]=1;
}
queue<int>q;
int f[2000];
int ans;
void AC()
{
for(int i=0;i<10;i++)
{
if(ch[0][i])q.push(ch[0][i]);
}
while(!q.empty())
{
int tmp=q.front();q.pop();
for(int i=0;i<10;i++)
{
if(ch[tmp][i])
{
f[ch[tmp][i]]=ch[f[tmp]][i];
q.push(ch[tmp][i]);
if(v[f[ch[tmp][i]]])v[ch[tmp][i]]=1;
}
else
{
ch[tmp][i]=ch[f[tmp]][i];
}
}
}
}
int now[2000][2],pre[2000][2];
char a[2000];
int be[2000];
int main()
{
scanf("%s",a);
n=strlen(a);
scanf("%d",&m);
for(int i=1;i<=m;i++)
{
scanf("%s",s);
build();
}
AC();
pre[0][0]=1;
for(int i=0;i<n;i++)
{
int x=a[i]-'0';
if(i)pre[0][1]++;
for(int j=0;j<=cnt;j++)
{
if(v[j])continue;
for(int k=0;k<x;k++)
{
if(!i&&!k)continue;
now[ch[j][k]][1]+=pre[j][0];
now[ch[j][k]][1]%=p;
}
now[ch[j][x]][0]+=pre[j][0];
now[ch[j][x]][0]%=p;
for(int k=0;k<10;k++)
{
now[ch[j][k]][1]+=pre[j][1]-(!j&&k==0&&i!=0);
now[ch[j][k]][1]%=p;
}
}
for(int j=0;j<=cnt;j++)
{
if(v[j])continue;
if(i==n-1)
{
ans+=now[j][1];
if(ans>=p)ans-=p;
ans+=now[j][0];
if(ans>=p)ans-=p;
}
pre[j][0]=now[j][0];
pre[j][1]=now[j][1];
now[j][0]=now[j][1]=0;
}
}
printf("%d\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: