628D Magic Numbers

传送门

题目大意

定义n-magic为从左往右,偶数位置均为n,奇数位置不为n的一类数。求出[a,b]内所有可被m整除的d-magic个数。

分析

显然是数位dp,我们用dp[i][j][k]表示考虑到第i位,小于还是等于范围,对m取模的余数为k的时候的个数,然后我们枚举所有满足情况的j(i为奇数则j不能为d,i为偶数则j只能为d)进行转移,转移为经典的数位dp转移。最后记得因为答案取模过所以可能在相减后变为负数因此要进行一下特殊处理。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<cctype>
#include<cmath>
#include<cstdlib>
#include<queue>
#include<ctime>
#include<vector>
#include<set>
#include<map>
#include<stack>
using namespace std;
const int mod = 1e9+7;
int d,M,dp[2100][2][2100],a[2100],cnt,m[2100];
char s[2100];
int go(){
      int i,j,k;
      scanf("%s",s);
      cnt=strlen(s);
      m[0]=0;
      for(i=0;i<cnt;i++){
          a[i+1]=(s[i]-'0');
          m[i+1]=(m[i]*10+a[i+1])%M;
      }
      memset(dp,0,sizeof(dp));
      dp[0][0][0]=1;
      for(i=1;i<=cnt;i++){
          for(j=0;j<=a[i];j++){
            if((i&1)&&j==d)continue;
            if(!(i&1)&&j!=d)continue;
            k=m[i-1];
            int x=(k*10+j)%M;
            if(j==a[i])dp[i][0][x]=(dp[i][0][x]+dp[i-1][0][k])%mod;
            else dp[i][1][x]=(dp[i][1][x]+dp[i-1][0][k])%mod;
          }
          for(j=0;j<=9;j++)
            for(k=0;k<M;k++){
                if((i&1)&&j==d)continue;
                if(!(i&1)&&j!=d)continue;
                int x=(k*10+j)%M;
                dp[i][1][x]=(dp[i][1][x]+dp[i-1][1][k])%mod;
            }
      }
      return (dp[cnt][0][0]+dp[cnt][1][0])%mod;
}
int ck(){
      int ok=1,sum=0;
      for(int i=1;i<=cnt;i+=2){
        sum=((sum<<3)+(sum<<1)+a[i])%M;
        if(a[i]==d)return 0;
      }
      for(int i=2;i<=cnt;i+=2){
        sum=((sum<<3)+(sum<<1)+a[i])%M;
        if(a[i]!=d){
          ok=0;
          break;
        }
      }
      if(sum)ok=0;
      return ok;
}
int main(){
      scanf("%d%d",&M,&d);
      cout<<abs(go()-ck()-go()-mod)%mod<<endl;
      return 0;
}
原文地址:https://www.cnblogs.com/yzxverygood/p/9494087.html