HDU 4352 XHXJ's LIS

分析

这道题来说的话其实不是很难,数据大到已经快炸long long了,不是规律就是数位,这肯定就是数位dp了啊。
那么我们来考虑状态需要什么,根据数位dp的板子,应该有当前还剩几位和限制条件,因为我们要记忆化所以肯定把k加到状态里,不然加着加着就乱了。其实这个题的主要问题还是最长上升子序列,观察一下这个序列有什么特点,首先,只有0~9,其次,严格单调上升,就是说不能有重复的数字,算LIS的几种方法里边,只要改良一下二分的方法就行,想一下那个方法里边为什么用二分,为了快速找到大于该数字的第一个数字,为什么要找这个数字,因为这个数字本身就无法更新答案,替换掉可能会使得后边的答案更优,所以就换掉呗。那对于这个题来说可以借鉴一下思想,但是没必要二分,当然如果你想应该也可以,我觉得应该没人对二分痴迷到10个数也去二分。。。。。
所以就把这十个数压到一个二进制的状态里,第i位的0/1就表示这个数字在序列中没出现过或出现过,然后就套一个数位dp的板子就行。
有一点注意一下吧还是,原来我在记忆化的时候,都是默认dp数组为0,也没T过,但这道题啊。。。。会T的,因为会有大量的答案为0的东西,每个都算就会T,所以以后还是多写几句初始化成-1吧。

#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int N=20;
ll dp[N][1<<10][N];
int num[N],len,k;
int find(int x){
    int cnt=0;
    while(x){
        cnt+=x&1;
        x>>=1;
    }
    return cnt;
}
int updata(int x,int sta){
    for(int i=x;i<=9;i++)
        if(1<<i&sta)return sta^(1<<i)|1<<x;
    return sta|1<<x;
}
ll dfs(int pos,int sta,bool ismx,bool isz){
    if(pos==0)return find(sta)==k;
    if(!ismx&&dp[pos][sta][k]!=-1)return dp[pos][sta][k];
    int mx=ismx?num[pos]:9;
    ll ans=0;
    for(int i=0;i<=mx;i++){
        ans+=dfs(pos-1,isz&&i==0?0:updata(i,sta),ismx&&i==mx,isz&&i==0);
    }
    if(!ismx)dp[pos][sta][k]=ans;
    return ans;
}
ll calc(ll u){
    len=0;
    while(u){
        num[++len]=u%10;
        u/=10;
    }
    return dfs(len,0,1,1);
}
int main(){
    int T;
    scanf("%d",&T);
    memset(dp,-1,sizeof(dp));
    for(int i=1;i<=T;i++){
        ll a,b;
        scanf("%lld%lld%d",&a,&b,&k);
        printf("Case #%d: %lld\n",i,calc(b)-calc(a-1));
    }
}
posted @ 2020-04-22 10:33  An_Fly  阅读(103)  评论(0编辑  收藏  举报