【ARC075F】Mirrored 搜索/数位dp

Description

​ 给定正整数DD,求有多少个正整数NN,满足rev(N)=N+Drev(N)=N+D,其中rev(N)rev(N)表示将NN的十进制表示翻转来读得到的数

Input

​ 一个正整数DD

Output

​ 满足上述条件的正整数的个数

Sample Input

Case 1:
63

Case 2:
75

Case 3:
864197532

Sample Output

Case 1:
2

Case 2:
0

Case 3:
1920

HINT

​ 1≤D≤1091≤D≤109

​ 样例1解释:81=18+63,92=29+63

Sol

我们把题目转化成rev(n)-n=d,然后折半搜索,只搜一半,另一半可以直接计算,这样的复杂度为\(O(2^{18})\)左右。

然而还是有更强的做法的,这个不用搜。。可以dp。。而且复杂度只有\(O(d^2*10)\),具体地,在这里:orzDTZ,写得非常详细。

Code

搜索:

#include <bits/stdc++.h>
#define ll long long
using namespace std;
int D,cnt[19],ans;ll b[20];
void dfs(int x,ll res,int L,int now)
{
    if(x>(L>>1)-1){if(res==D) ans+=now*(L&1?10:1);return;}
    ll i=-9ll;while(i<9ll&&res+(i+1ll)*(b[L-x-1]-b[x])<=D) ++i;
    dfs(x+1,res+i*(b[L-x-1]-b[x]),L,now*(x==0&&i>=0?cnt[i+9]-1:cnt[i+9]));
    if(++i<=9) dfs(x+1,res+i*(b[L-x-1]-b[x]),L,now*(x==0&&i>=0?cnt[i+9]-1:cnt[i+9]));
}
int main()
{
    scanf("%d",&D);
    b[0]=1ll;for(int i=1;i<19;++i) b[i]=b[i-1]*10ll;
    for(int i=0;i<=9;++i) for(int j=0;j<=9;++j) ++cnt[i-j+9];
    for(int i=1;i<=18;++i) dfs(0,0,i,1);
    printf("%d",ans);
}

dp:

#include <bits/stdc++.h>
using namespace std;
int d[10005],f[10005][2][2],L,ans,lim,P=1e9+7;char D[10005];
int dp(int n)
{
    int m=n>>1,res=0;
    for(int i=0;i<=m;i++) for(int j=0;j<2;j++) for(int k=0;k<2;k++) f[i][j][k]=0;
    f[0][0][0]=1;
    for(int i=0;i<m;i++) for(int j=0;j<2;j++) for(int k=0;k<2;k++) if(f[i][j][k]) for(int x=0,y,j1,k1;x<10;x++)
    {
        k1=x+d[i+1]+k,y=k1%10,k1/=10,j1=10*j+x-y-d[n-i];
        if(j1<0||j1>1||(!i&&(!x||!y))) continue;
        (f[i+1][j1][k1]+=f[i][j][k])%=P;
    }
    if(n&1) for(int j=0,mid=(n+1)>>1;j<2;j++) for(int k=0;k<2;k++) if(f[m][j][k]) for(int x=0,y;x<10;x++)
    {
        y=x+d[mid]+k;
        if((x==y%10)&&(y/10==j)) (res+=f[m][j][k])%=P;
    }
    if(!(n&1)) for(int j=0;j<2;j++) (res+=f[m][j][j])%=P;
    return res;
}
int main()
{
    scanf("%s",D+1);L=strlen(D+1);lim=L<<1;
    for(int i=1;i<=L;i++) d[L-i+1]=D[i]-'0';
    for(int i=max(2,L);i<=lim;i++) (ans+=dp(i))%=P;
    printf("%d\n",(ans+P)%P);
}

转载于:https://www.cnblogs.com/CK6100LGEV2/p/9476170.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值