Palindrome Basis

这篇博客讨论了一个数学问题,即如何计算将一个正整数表示为若干个正回文数之和的不同方式数量。回文数是指正向和反向读取都相同的数字。作者解释了如何通过预处理确定哪些数字是回文数,并使用动态规划的方法,特别是完全背包的概念,来解决这个问题。示例中展示了对于输入数字5和12的解题过程,分别有7种不同的分拆方式。文章强调了在动态规划过程中,对于01背包和完全背包问题,遍历顺序的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

You are given a positive integer nn. Let's call some positive integer aa without leading zeroes palindromic if it remains the same after reversing the order of its digits. Find the number of distinct ways to express nn as a sum of positive palindromic integers. Two ways are considered different if the frequency of at least one palindromic integer is different in them. For example, 5=4+15=4+1 and 5=3+1+15=3+1+1 are considered different but 5=3+1+15=3+1+1 and 5=1+3+15=1+3+1 are considered the same.

Formally, you need to find the number of distinct multisets of positive palindromic integers the sum of which is equal to nn.

Since the answer can be quite large, print it modulo 109+7109+7.

Input

The first line of input contains a single integer tt (1≤t≤1041≤t≤104) denoting the number of testcases.

Each testcase contains a single line of input containing a single integer nn (1≤n≤4⋅1041≤n≤4⋅104) — the required sum of palindromic integers.

Output

For each testcase, print a single integer denoting the required answer modulo 109+7109+7.

Example

input

Copy

2
5
12

output

Copy

7
74

Note

For the first testcase, there are 77 ways to partition 55 as a sum of positive palindromic integers:

  • 5=1+1+1+1+15=1+1+1+1+1
  • 5=1+1+1+25=1+1+1+2
  • 5=1+2+25=1+2+2
  • 5=1+1+35=1+1+3
  • 5=2+35=2+3
  • 5=1+45=1+4
  • 5=55=5

For the second testcase, there are total 7777 ways to partition 1212 as a sum of positive integers but among them, the partitions 12=2+1012=2+10, 12=1+1+1012=1+1+10 and 12=1212=12 are not valid partitions of 1212 as a sum of positive palindromic integers because 1010 and 1212 are not palindromic. So, there are 7474 ways to partition 1212 as a sum of positive palindromic integers.

思路:刚开始时没有读明白题,没明白啥意思,后来才知道组成它的数是回文数即可。那么我们是不是只要通过预处理出哪个数回文的哪个数不是回文,然后让这些回文数进行组合成我们需要的数是不是就可以了。回文数:只要把这个数颠倒过来还是原来的那个数就是回文数。留下的回文数不就是让我们任意挑选的,只要相加不超过我们需要的那个数即可,这是不是就变成了选数选物品的问题了,这不就是完全背包问题了。

int转化为字符串:to_string方法

一维压缩dp数组,可以很好解决预处理问题,也就是不知道物品数量时(但是开到不越界的二维也是行的)

背包问题:

01背包:

//二维写法
for(int i=1;i<=n;i++)
{
    for(int j=0;j<=v;j++)
    {
        f[i][j]=f[i-1][j];
        if(a[i]<=j)
        {
            f[i][j]=max(f[i][j],f[i-1][j-a[i]]+w[i]);
        }
    }
}


//一维写法
for(int i=1;i<=n;i++)
{
    for(int j=m;j>=a[i];j--)
    {
        f[j]=max(f[j],f[j-a[i]]+w[i]);
    }
}

二维的不用考虑遍历的顺序(任何),为什么在压缩到一维后,第二层必须要倒着遍历呢?是因为防止多个相同物品的插入。例如:weight[0]=1 , value[0]=15。那么 f[1] = f[1-weight[0]] + value[0] = 15 , f[2] = f[2-weight[0]] + value[0] = 15+15=30.也就是在 j 循环扩大时,会不断多次选择这个较小的数,就会变成完全背包。可是当我们倒着遍历时:f[2] = f[2-weight[0]] + value[0] =15 , f[1] = f[1-weight[0]] + value[0] = 15 ,这样每一次的选择都是一个新的 f 数组,不会多次选择,不会用上一个的值来影响当前的数一维01背包倒着遍历。(为什么会有 f[i-1][j-x]+w 因为这个物品只能选一次,加上 w[i] 时就已经证明要选这个数了,而完全背包不会,只需要注意体积就行了

完全背包:

//二维写法
for(int i=1;i<=n;i++)
{
    for(int j=0;j<=v;j++)
    {
        f[i][j]=f[i-1][j];
        if(a[i]<=j)
        {
            f[i][j]=max(f[i][j],f[i][j-a[i]]+w[i]);
        }
    }
}



//一维写法
for(int i=1;i<=n;i++)
{
    for(int j=0;j<=v;j++)
    {
        f[j]=max(f[j],f[j-a[i]]+w[i]);
    }
}

选无数次正着遍历,只能选一次的倒着遍历不受影响。

完整代码:

#include <bits/stdc++.h>

using namespace std;

#define int long long
const int mod=1e9+7;

const int N=4e4+10;
int f[N];
vector<int>v;

void solve()
{
    string s,t;
    for(int i=1;i<=40000;i++)
    {
        s=to_string(i);
        t=s;
        reverse(t.begin(),t.end());
        if(s==t)v.push_back(i);
    }

    int len=v.size();
    f[0]=1;
    for(int i=1;i<=len;i++)
    {
        int x=v[i-1];
        for(int j=x;j<=40000;j++)
        {
            f[j]=(f[j]+f[j-x])%mod;
        }
    }
}

signed main() {
    ios_base::sync_with_stdio(false);
    cin.tie(NULL);
    int t;
    cin>>t;
    solve();
    while(t--)
    {
        int n;
        cin>>n;
        cout<<f[n]<<endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值