您的位置:首页 > 其它

bzoj3530: [Sdoi2014]数数

2017-02-08 18:05 246 查看

链接

  http://www.lydsy.com/JudgeOnline/problem.php?id=3530

题解

  一个小错查了一下午,建trie图的时候我把0当做串的终止,可题目中明明要求0是合法的。。。

  唉。。

  f[i][j]表示长度为i的数字匹配到j点的方案数(允许前导零没有大小限制),g[i][j]是有大小限制的。

  然后直接dp就行了(ac自动机上的dp道路+数位dp套路)。

  答案的统计:这个有点费脑子,枚举位数,枚举下一位从1~9,然后计入ans就行了。(其实不费脑子但是我今天有点智障)

代码

//AC自动机上的数位dp
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
#define maxn 2000
#define mod 1000000007
using namespace std;
int num[maxn], trie[maxn][10], tail[maxn], tot=1, fail[maxn], N, f[maxn][maxn],
g[maxn][maxn], h[maxn][maxn];
queue<int> q;
void insert(int *s)
{
int p;
for(p=1;*s!=-1 and !tail[p];s++)p=trie[p][*s]?trie[p][*s]:trie[p][*s]=++tot;
tail[p]=1;
}
int acamove(int pos, int x)
{
for(;pos and !trie[pos][x];pos=fail[pos]);
return pos?trie[pos][x]:1;
}
void acabuild()
{
int u, v, i;
q.push(1);
while(!q.empty())
{
u=q.front();q.pop();
for(i=0;i<=9;i++)
{
if(trie[u][i])
{
v=acamove(fail[u],i);
fail[trie[u][i]]=v;
q.push(trie[u][i]);
}
else trie[u][i]=acamove(u,i);
}
}
}
void input()
{
char s[maxn];
int t[maxn];
int M, i, j, l;
scanf("%s%d",s,&M);
N=strlen(s);
for(i=0;i<N;i++)num[N-i]=s[i]-48;
for(i=1;i<=M;i++)
{
scanf("%s",s);
l=strlen(s);
for(j=0;s[j];j++)t[j]=s[l-j-1]-48;t[l]=-1;
insert(t);
}
}
int dp()
{
int i, j, now, ans=0;
f[0][1]=g[0][1]=1;
for(i=0;i<N;i++)
for(j=1;j<=tot;j++)
{
if(tail[j] or (!f[i][j] and !g[i][j]))continue;
for(now=0;now<=9;now++)
f[i+1][trie[j][now]]=(f[i+1][trie[j][now]]+f[i][j])%mod;
for(now=0;now<=num[i+1];now++)
{
if(now!=num[i+1])
g[i+1][trie[j][now]]=(g[i+1][trie[j][now]]+f[i][j])%mod;
else g[i+1][trie[j][now]]=(g[i+1][trie[j][now]]+g[i][j])%mod;
}
if(i<N-1)
{
for(now=1;now<=9;now++)
if(!tail[trie[j][now]])ans=(ans+f[i][j])%mod;
}
else
{
for(now=1;now<=num
;now++)
{
if(tail[trie[j][now]])continue;
if(now==num
)ans=(ans+g[i][j])%mod;
else ans=(ans+f[i][j])%mod;
}
}
}
return ans;
}
int main()
{
input();
acabuild();
printf("%d",dp());
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: