数位DP Increasing or Decreasing

Increasing or Decreasing

We all like monotonic things, and solved many problems about that like Longest Increasing Subsequence

(LIS). Here is another one which is easier than LIS (in my opinion).
We say an integer is a momo number if its decimal representation is monotonic. For example, 123, 321,777 and 5566 are momo numbers; But 514, 50216 and 120908 are not.
Please answer m queries. The i-th query is a interval [li, ri], and please calculate the number of momo numbers in it.
Input


The first line contains an integer m.
Each of the following m lines contains two integers li , ri .


• 1 ≤ m ≤ 105
• 1 ≤ li ≤ ri ≤ 1018


Output


For each query, please output the number of momo numbers in that range.
Sample input 


2
1 100
100 200


Sample output
100

48


题目大意:

定义一类momo number:各数位从高位到低位单调不减或者单调不增,给出L,R,问区间[L,R]之间momo number的数量。

我的做法是用数位DP统计从0到R,L-1的momo number,再相减得到答案。

至于如何得到0到N,我是先统计以x结尾长度为y的单调不升的方案数,和以x结尾长度为y的单调不降的方案数,然后先获得位数比当前数字位数低的momo number的数量,再从高位依次枚举到低位,枚举当前位的数字统计momo number的数量,统计方法是依次固定前几个高位的数字再枚举当前位的数字值就可以不重不漏的统计,但是要注意的是,如果前面固定的数字出现了先上升再下降或者先下降再上升的情况,就立即退出统计。

代码:

#include <bits/stdc++.h>
using namespace std;

inline void read(int &x){
    char ch;
    bool flag=false;
    for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;
    for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
    x=flag?-x:x;
}


inline void read(long long  &x){
    char ch;
    bool flag=false;
    for (ch=getchar();!isdigit(ch);ch=getchar())if (ch=='-') flag=true;
    for (x=0;isdigit(ch);x=x*10+ch-'0',ch=getchar());
    x=flag?-x:x;
}

const int maxn=30;
int fdown[maxn][11];
int fup[maxn][11];

void prepare(){
for (int i=0;i<=9;i++)
    {
        fdown[1][i]=1;
        fup[1][i]=1;
    }
for (int i=1;i<=20;i++)
    for (int j=0;j<=9;j++)
        for (int k=j;k<=9;k++)
            fdown[i][j]+=fdown[ i-1 ][k];
for (int i=1;i<=20;i++)
    for (int j=0;j<=9;j++)
        for (int k=0;k<=j;k++)
            fup[i][j]+=fup[i-1][k];
}

int query(long long x){
if (x<=10)
    return x+1;
int num[20];
int cnt=0;
long long tmp=1;
while (x)
    {
        num[++cnt]=x%10;
        x=x/10;
        tmp=tmp*10;
    }
reverse(num+1,num+cnt+1);
int ans=0;
ans=query(tmp/10-1);
int op=0;
bool is=1;
for (int i=1;i<=cnt;i++)
    {

        bool vis1=0,vis2=0;
        if (op!=1)
        if (i!=1)
            {
                for (int j=0;j <=min( num[i-1] , num[i]-1) ;j++)
                {
                    ans+=fup[cnt-i+1][j];
                    vis1=1;
                }
            }
        else
            for (int j=1;j < num[i];j++)
            {
                ans+=fup[cnt-i+1][j];
                vis1=1;
            }
        //printf("down  %d\n",ans);

        if (op!=-1)
        if (i!=1)
            {
                for (int j=num[i-1];j < num[i] ;j++)
                {
                    //printf("%d %d\n",cnt-i+1,fdown[cnt-i+1][j]);
                    ans+=fdown[cnt-i+1][j];
                    vis2=1;
                }
            }
        else
            for (int j=1;j < num[i]; j++)
            {
                ans+=fdown[cnt-i+1][j];
                //printf("%d %d\n",cnt-i+1,fdown[cnt-i+1][j]);
                vis2=1;
            }

        //printf("up  %d\n",ans);
        if (op==0)
            {
                if (i!=1)
                {
                    if ( num[i] < num[i-1])
                        op=-1;
                    if ( num[i] > num[i-1])
                        op=1;
                }
            }
        if (i==1)
            ans-=num[i]-1;
        else
            if ( vis1 && vis2)
                ans--;
        if (op==1)
            {
                if ( num[i] < num[i-1] )
                {
                    is=0;
                    break;
                }
            }
        if (op==-1)
            {
                if ( num[i] > num[i-1] )
                {
                    is=0;
                    break;
                }
            }
        //printf("      %d\n",ans);
    }
if (is)
    ans++;
return ans;
}


int main(){
    //freopen("mine.out","w",stdout);
    prepare();
    //printf("%d\n",query(212));
    //puts("\n");
    //printf("%d\n",query(211));
    //int N=1000000;
    //for (int i=0;i<=N;i++)
        //printf("%d\n",query(i));
    int T;
    read(T);
    for (int i=1;i<=T;i++)
        {
            long long l,r;
            read(l); read(r);
            printf("%d\n",query(r)-query(l-1));
        }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值