ZCMU—1781

来源:互联网 发布:打谱软件哪个好索呢 编辑:程序博客网 时间:2024/04/30 15:41

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的最长上升序列算法...夸一下大佬强硬的算法...萌新瑟瑟发抖...
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);     }}


0 0
原创粉丝点击