T12 XHXJ's LIS (HDU4352)
T12 XHXJ's LIS (HDU4352)
经过前面几题,想必大家对状压很熟悉了
HDU传送门
题意简述
求区间
- 延伸:在区间
[L,R] 中任意选出一个数,求各位数字组成的序列的LIS (最长上升子序列)的期望值。
数据范围
题解
思路
和前面几题类似,还是考虑对于单个数如何求各位数组成的
但是这里不能用那个
有一种
一个一个地加入,用
如果加入的这个数是最大的,那么
否则,在前面的
为什么呢?
-
对于
i 前面的,既然a[j] 已经是大于等于a[i] 中最小的了,它变成a[i] 不会改变大小关系,也不会影响答案。 -
对于
i 后面的,(如果不换)选用a[i] 显然比选用a[j] 更优,替换掉相当于等效
举个栗子,
-
加入2, 5,
f[1]=2 ,f[2]=5 -
加入4,4之前最小的
\geq4 的是5,因此把5换成4,24413,f[2]=4 -
加入1,1之前最小的
\geq1 的是2,因此把2换成1,14413 -
加入3,3之前最小的
\geq3 的是4,因此把4都换成3,13313 ,f[2]=3 -
所以
LCS=2
当然光是这样空间是
其实这种dp有个特性,它只关心每个数有没有出现过,却不关心具体的位置与个数
于是我们可以直接记录每个数是否出现过,空间只有
最后的
这是状压部分,当然既然要求
发现状压和数位的加入顺序都是从高位到低位,直接dp就好了
做法
设
不过询问数有
不如再加一维
数位dp我想不用再细说了吧
注意事项
-
这里前导0是有影响的,注意判断
-
状态更新时注意如果原来是0,加入0是无需更新的(要特判)
AC代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
ll t,l,r,k;
ll a[22],f[22][1100][11];
ll dp(ll pos,ll mac,bool lead,bool eq){//数位dp模板
if(pos==0){//如果枚举完了
ll tot=0;
for(ll i=0;i<=9;i++) if((mac>>i)&1) tot++;
if(tot==k) return 1;//如果LIS为k
else return 0;
}
if(!eq && !lead && f[pos][mac][k]!=-1) return f[pos][mac][k];
ll ed=eq?a[pos]:9,ret=0;//模板
for(ll i=0;i<=ed;i++){
ll new_mac=mac;
if(!lead || i){//注意不为0或加上的数不为0才需要转移
for(ll j=i;j<=9;j++)
if((mac>>j)&1){
new_mac^=(1<<j);
break;
}
new_mac|=(1<<i);
}
ret+=dp(pos-1,new_mac,lead && !i,eq && i==a[pos]);
}
if(!eq && !lead) f[pos][mac][k]=ret;
return ret;
}
ll solve(ll x){//数位dp模板
memset(a,0,sizeof(a));
while(x){
a[++a[0]]=x%10;
x/=10;
}
return dp(a[0],0,1,1);
}
int main(){
memset(f,-1,sizeof(f));//注意初始化
scanf("%lld",&t);
for(ll i=1;i<=t;i++){
scanf("%lld%lld%lld",&l,&r,&k);
printf("Case #%lld: %lld\n",i,solve(r)-solve(l-1));
}
}