Confusing Problem

北邮秋季赛  Confusing Problem


这个题比赛的时候没想出来,当时以为是数位dp,但是状态几乎都没法表示呀,单纯的以为  单独匹配A + 匹配B的 - 匹配A和B的,然后就没有然后了……

赛后看题解说是ac自动机+数位dp,猛然发现用ac自动机来构造状态时再合适不过了,题解是这样说的:


B 数位dp。

状态是dfs(i, m, s, e),i为当前做到前i位,m为是否已经匹配了两个串,s为走到自动机的节点s(或者kmp也能做),e为前i位是否是上限。


但是我敲了,仔细调试发现光有这些状态还是不行的,还要考虑到前缀是否为0,如果为0 ,那么当前i位可以为0,否则就必须从1开始

然后状态表示就是: dp[ pos ][ state] [ match ] [  zero ]  表示当前做到第i位,走到ac自动机state节点,目前是否匹配A或者B,前缀是否为0的个数


#include <cmath>
#include <cstdio>
#include <iostream>
#include <cstring>

using namespace std;

typedef long long ll;
const int NODE=150;
int chd[NODE][10],word[NODE],fail[NODE],sz;
int dig[50];
ll dp[50][NODE][2][2];

void ins(ll n)
{
    int pos=0;
    for(;n;n/=10) dig[pos++]=n%10;
    int p=0;
    for(int i=pos-1;i>=0;i--)
    {
        if(!chd[p][dig[i]])
        {
            memset(chd[sz],0,sizeof(chd[sz]));
            word[sz]=0;
            chd[p][dig[i]]=sz++;
        }
        p=chd[p][dig[i]];
    }
    word[p]=1;
}
int Que[NODE];
void ac()
{
    int *s=Que,*e=Que;
    for(int i=0;i<10;i++)
      if(chd[0][i]){
          *e++=chd[0][i];
          fail[chd[0][i]]=0;
      }
    while(s!=e)
    {
        int p=*s++;
        for(int i=0;i<10;i++)
          if(chd[p][i]){
             *e++=chd[p][i];
             fail[chd[p][i]]=chd[fail[p]][i];
             word[chd[p][i]]|=word[fail[chd[p][i]]];
          }else chd[p][i]=chd[fail[p]][i];
    }
}

ll dfs(int pos,int ok,int state,bool zero,bool doing)
{
    if(pos<0) return ok;
    if(!doing&&dp[pos][state][ok][zero]!=-1) return dp[pos][state][ok][zero];

    int end=doing?dig[pos]:9;
    ll ret=0;
    for(int i=zero?0:1;i<=end;i++)
    {
        int newstate=chd[state][i];
        ret+=dfs(pos-1,ok|word[newstate],newstate,zero&&!i,doing&&i==end);
    }
    if(!doing) dp[pos][state][ok][zero]=ret;
    return ret;
}

ll cal(ll n)
{
    int pos=0;
    for(;n;n/=10) dig[pos++]=n%10;
    return dfs(pos-1,0,0,1,1);
}

int main()
{
    ll L,R,A,B;
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld%lld %lld%lld",&L,&R,&A,&B);
        sz=1;
        memset(chd[0],0,sizeof(chd[0]));
        ins(A);
        ins(B);
        ac();
        memset(dp,-1,sizeof(dp));
        printf("%lld\n",cal(R)-cal(L-1));
    }
    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值