HDU 4352 XHXJ's LIS

题目

题意:定义一个数字的LIS为将其看成一个字符串的LIS,问([l,r])内有多少数字的LIS长度为(k)

考虑树状数组求一个序列LIS的过程,我们定义了一个东西(f_i)表示结尾元素不超过(i)的LIS的最大长度,维护这个数组就能实现较为快速的转移

显然这个数组是单调不降的,且不难发现(f_i-f_{i-1}leq 1),于是差分之后就只会存在(0,1)两种元素

于是我们在数位dp的过程中把差分序列记下来作为dp的状态就好了,注意前导零的问题,以及预处理转移会更快

代码

#include<cstdio>
#include<cstring>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
LL L,R;int k,m,lm[66];
int id[(1<<10)+5],st[11][(1<<10)+5],cnt[(1<<10)+5];
LL dp[2][(1<<10)+5][2];
int A[(1<<10)+5][11],tr[(1<<10)+5][11],B[11],M[11];
inline void trans(int state) {
	for(re int i=0;i<10;++i)A[m][i]=state>>i&1;
	for(re int i=1;i<10;++i)A[m][i]+=A[m][i-1]; 
}
inline void Pre_tr(int x) {
	for(re int i=0;i<10;i++) {
		for(re int j=0;j<10;j++)B[j]=A[x][j];
		B[i]=max(B[i],(i>0?A[x][i-1]:0)+1);
		for(re int j=1;j<10;j++)B[j]=max(B[j-1],B[j]);
		tr[x][i]=B[0];
		for(re int j=1;j<10;j++) tr[x][i]|=((B[j]-B[j-1])<<j);
		tr[x][i]=id[tr[x][i]];
	}
}
void dfs(int x,int state,int res) {
	if(res<0) return;
	if(x==10) {
		id[state]=++m;trans(state);
		cnt[m]=res;st[res][++M[res]]=m;
		return;
	}
	dfs(x+1,state,res);
	dfs(x+1,state|(1<<x),res+1);
}
inline LL calc(LL N) {
	if(!N)return 0;int tot=0;
	while(N) lm[++tot]=N%10ll,N/=10ll;
	int o=0;memset(dp,0,sizeof(dp));
	for(re int i=tot;i;--i,o^=1) {
		dp[o][id[0]][i==tot?1:0]++;
		for(re int j=1;j<=m;j++) dp[o^1][j][0]=dp[o^1][j][1]=0;
		for(re int j=1;j<=m;j++) {
			if(!dp[o][j][0]) continue;
			if(cnt[j]>=k) continue;
			for(re int k=0;k<10;++k)
				if(tr[j][k]) dp[o^1][tr[j][k]][0]+=dp[o][j][0];
		}
		for(re int j=1;j<=m;j++) {
			if(!dp[o][j][1]) continue;
			if(cnt[j]>=k) continue;
			for(re int k=0;k<lm[i];++k)
				if(tr[j][k]) dp[o^1][tr[j][k]][0]+=dp[o][j][1];
			if(tr[j][lm[i]]) dp[o^1][tr[j][lm[i]]][1]+=dp[o][j][1];
		}
		dp[o^1][tr[id[0]][0]][lm[i]?0:1]--;
	}
	LL ans=0;
	for(re int i=1;i<=M[k];i++) ans+=dp[o][st[k][i]][0]+dp[o][st[k][i]][1];
	return ans;
}
int main() {
	m=0;dfs(0,0,0);
	for(re int i=1;i<=m;i++) Pre_tr(i);
	int T;scanf("%d",&T);
	for(re int tnum=1;tnum<=T;++tnum) {
		scanf("%lld%lld%d",&L,&R,&k);
		printf("Case #%d: %lld
",tnum,calc(R)-calc(L-1));
	}
	return 0;
}

之后这玩意就T没了,原因是(Tleq 10^4),实在是不小;于是改成记忆化才行

#include<cstdio>
#include<cstring>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
LL L,R;int k,m,lm[66];
int id[(1<<10)+5],cnt[(1<<10)+5];
LL dp[20][(1<<10)+5][11];
int A[(1<<10)+5][11],tr[(1<<10)+5][11],B[11],M[11];
inline void trans(int state) {
	for(re int i=0;i<10;++i)A[m][i]=state>>i&1;
	for(re int i=1;i<10;++i)A[m][i]+=A[m][i-1]; 
}
inline void Pre_tr(int x) {
	for(re int i=0;i<10;i++) {
		for(re int j=0;j<10;j++)B[j]=A[x][j];
		B[i]=max(B[i],(i>0?A[x][i-1]:0)+1);
		for(re int j=1;j<10;j++)B[j]=max(B[j-1],B[j]);
		tr[x][i]=B[0];
		for(re int j=1;j<10;j++) tr[x][i]|=((B[j]-B[j-1])<<j);
		tr[x][i]=id[tr[x][i]];
	}
}
void dfs(int x,int state,int res) {
	if(res<0) return;
	if(x==10) {
		id[state]=++m;trans(state);cnt[m]=res;
		return;
	}
	dfs(x+1,state,res);
	dfs(x+1,state|(1<<x),res+1);
}
LL DFS(int len,int lim,int s) {
	if(!len||cnt[s]>k) return cnt[s]==k;
	if(!lim&&dp[len][s][k]!=-1) return dp[len][s][k];
	LL ans=0;
	for(re int i=0;i<=(lim?lm[len]:9);++i)
		ans+=DFS(len-1,lim&&(i==lm[len]),tr[s][i]);
	if(!lim) dp[len][s][k]=ans;
	return ans;
}
inline LL calc(LL N) {
	if(!N)return 0;int tot=0;
	while(N) lm[++tot]=N%10ll,N/=10ll;
	return DFS(tot,1,id[0]);
}
int main() {
	m=0;dfs(0,0,0);memset(dp,-1,sizeof(dp));
	for(re int i=1;i<=m;i++) Pre_tr(i);tr[id[0]][0]=id[0];
	int T;scanf("%d",&T);
	for(re int tnum=1;tnum<=T;++tnum) {
		scanf("%lld%lld%d",&L,&R,&k);
		printf("Case #%d: %lld
",tnum,calc(R)-calc(L-1));
	}
	return 0;
}
//g++ hdu4352.cpp -o hdu4352
原文地址:https://www.cnblogs.com/asuldb/p/12141735.html