XHXJ's LIS

刚发现我写题解从来都没有把题目名字写出来过,从今天开始起补上

又是一个新的数位DP姿势,学完各位大神的博客来自己总结一发


题目链接:HDOJ4352

题意:给定【L,R】,求各个数的数位上数字的LIS为K的数有多少个,看到L,R的数值大小就知道是数位DP,看到K最大是10,也可以猜到K是dp中状态的一维,但是怎么放进去呢?


先想想模板,dp【pos】【status】肯定还是需要的,pos是数位,那么status怎么定义呢?

此题状态定义显然与LIS有关,K最多为10,想到状态压缩,每一个数字0-9是否出现,在status用0或者1表示,所以一个整数可以表示出0-9是否出现过

LIS有种O(nlogn)的做法:

dp【i】:记录LIS长度为i的时候,最后一个数的大小,越小越好的思路

借鉴到这道题来的时候,需要让出现的数字尽可能的小,所以status中,低位出现的1就要尽量多(这就可以写个函数来实现)


既然是LIS长度为K,那么K肯定需要是dp中的一维状态变量

所以状态量定义为dp【pos】【status】【K】,其中status中1的个数就是LIS的长度


然后呢,在LIS中有个特殊情况必须处理好。如果某个数位中已经出现了0,那么之前的LIS就可以断下来了(因为再也不可能更长)

所以在记忆化的时候,需要一个变量来记录是不是当前有0,或者说前导0


剩下的贴代码可以直接懂了,跟模板没有太多区别了

#include<map>
#include<set>
#include<math.h>
#include<time.h>
#include<iostream>
#include<cstdio>
#include<queue>
#include<stack>
#include<stdio.h>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<cstdlib>
using namespace std;

#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define ll rt<<1
#define rr rt<<1|1
#define LL long long
#define ULL unsigned long long
#define maxn 1050
#define maxnum 1000050
#define eps 1e-6
#define input freopen("input.txt","r",stdin)
#define output freopen("output.txt","w",stdout)

/*
函数解释:
getnewstatus:
LIS的O(nlogn)

getonenum:
计算s中有多少个1 
*/

int digit[50],K;
__int64 dp[50][1<<12][12];
__int64 L,R;

int getnewstatus(int x,int s){
	for(int i=x;i<10;i++)
		if (s&(1<<i)) return (s^(1<<i))|(1<<x);
	//如果在某个高位有可以替代的值
	//用x这位去替代i这一位
	//数值会是更小的 
	return s|(1<<x);
	//找不到的话,就把第x这位添加进去 
}

int getonenum(int s){
	//实参代入时为status 
	int num=0;
	while(s){
		if (s%2) num++;
		s>>=1; 
	}
	return num;
}

__int64 dfs(int pos,int status,bool flag,bool zero){
	if (pos==0) return getonenum(status)==K;
	if (flag&&dp[pos][status][K]!=-1) return dp[pos][status][K];
	int num=flag?9:digit[pos];
	__int64 ans=0;
	for(int i=0;i<=num;i++)
	//注意到0这个特殊数值 
		ans+=dfs(pos-1,zero&&(i==0)?0:getnewstatus(i,status),flag||i<num,zero&&(i==0));
	if (flag) dp[pos][status][K]=ans;
	return ans;
}

__int64 calc(__int64 n){
	int len=0;
	while(n){
		digit[++len]=n%10;
		n/=10;
	}
	return dfs(len,0,0,1);
}

int main(){
	//input;
	memset(dp,-1,sizeof(dp));
	int T;
	scanf("%d",&T);
	for(int Case=1;Case<=T;Case++){
		memset(digit,0,sizeof(digit));
		scanf("%I64d%I64d%d",&L,&R,&K);
		printf("Case #%d: %I64d\n",Case,calc(R)-calc(L-1));
	}
	return 0;
}