ZCMU—1781

1781: 上升序列数

Time Limit: 1 Sec   Memory Limit: 128 MB
[ Submit][ Status][ Web Board]

Description

假设有一个数列1,4,5,6,6,7那么这个数列的严格上升子序列就是1,4,5,6,7。现在有一个数比如123,如果单独看123的每一位就是1,2,3,
那么我们就能得到长度是3的(1,2,3)的严格上升子序列。假设一个数的最长的严格上升子序列长度是k,那么这个数就是k魅力数,现在我需
要计算区间[L,R]中有几个k魅力数?

Input

一个T(T<=10000),接下来T行,每行三个数L,R,K(0<L<=R<2^63.1<=K<=10)

Output

Case #x: y,x是测试编号从1开始,y表示答案

Sample Input

1
123 321 2

Sample Output

Case #1: 139

【分析】

....一开始以为是构造算答案,但是发现数又大,区间也大,一旦跨长度就会变得复杂。
后来想了半个多小时...决定...硬干....
有一点,如果想到了就可以下手,就是,为什么要考虑区间内有多少数呢?用区间和的思想,我只要算出1~l-1有多少k魅力数,和1~r有多少k魅力数,然后直接减一下不就可以做一次优化了吗?
数位dp,记得状态压缩...然后加上简单的bfs记录答案
当然这里需要用到一点,就是nlogn的最长上升序列算法...夸一下大佬强硬的算法...萌新瑟瑟发抖...
f[i][j][k]表示i为当前进行到的数字位置,j是用来压缩状态的,因为严格上升,所以每个数只会出现一次用0000000000来表示第i个数是否出现,1出现0没出现,其中1的个数就是最长上升序列长度,k表示要求的上升序列长度
【代码】
#include <stdio.h>
#include <string.h>
int a[1000];
long long n,m;
int k;
int len;
long long f[30][1100][20];
 
 
int gg(int x,int s)
{
    for (int i=x;i<=9;i++)
        if (s &(1<<i))
            return (s^(1<<i))|(1<<x);
    return s|(1<<x);
}
 
int qq(int x)
{
    int ans=0;
    while (x)
    {
        if (x&1) ans++;
        x>>=1;
    }
    return ans;
}
 
long long judge(int deep,int s,bool flag,bool z)//flag记录是否到头,z表示前一位是否为0,也就是前一位数字是否被使用
{
    if (deep==-1) return qq(s)==k;
    if (!flag && f[deep][s][k]!=-1) return f[deep][s][k];
    long long ans=0;
    int end=flag?a[deep]:9;
    for (int i=0;i<=end;i++)
        ans+=judge(deep-1,(z&&i==0)?0:gg(i,s),flag&&i==end,z&&(i==0));
    if (!flag) f[deep][s][k]=ans;
    return ans;
}
 
int main()
{
    memset(f,-1,sizeof(f));
    int pp;scanf("%d",&pp);
    for (int p=1;p<=pp;p++)
    {
        scanf("%lld%lld%d",&n,&m,&k);
        len=0;n--;
        while (n)
        {
            a[len++]=n%10;
            n/=10;
        }
        long long x1=judge(len-1,0,1,1);
        len=0;
        while (m)
        {
            a[len++]=m%10;
            m/=10;
        }
        long long x2=judge(len-1,0,1,1);
        printf("Case #%d: %lld\n",p,x2-x1);
     }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值