HDU 4352:XHXJ's LIS
题目:(原题是英文而且很迷) 求区间内数的LIS长度==k的个数,比如153948的LIS为1 3 4 8,长度为4。据说这种题叫DP中DP,本来是线性,再套一层状压+数位,简直厉害到不行……
线性的部分为O(nlogn)的LIS。比如现在找出的序列为1 3 4 8,两个策略,如果再来一个2就变成1 2 4 8(二分找第一个比它大的数),再来9就变成1 2 4 8 9(更新长度)。代码在下面
1 #include<cstdio> 2 #include<ctime> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 using namespace std; 7 int n,f[1010],x,l,r,len; 8 int main() 9 { 10 scanf("%d",&n); 11 f[0]=-1; 12 for(int i=1;i<=n;++i){ 13 scanf("%d",&x); 14 l=1;r=len; 15 if(x>f[len]){ 16 f[++len]=x; 17 continue; 18 } 19 while(l<r){ 20 int mid=(l+r)>>1; 21 if(x>f[mid]) l=mid+1; 22 else r=mid; 23 } 24 f[l]=x; 25 } 26 printf("%d",len); 27 return 0; 28 }
然后我们就要数位里状压了。我们知道对于0~9的LIS最长是10,那就可以用二进制来表示LIS的状态。还用上面的例子1 3 4 8,表示为0100011010。然后dp的时候发现状态的更新并没有那么方便,不过我们可以预处理LIS的更新情况,建立状态之间的联系,比如1 3 4 8--插入2-->1 2 4 8。之后直接套进数位DP的板子就做完了,并没有看起来那么难。
1 #include<cstdio> 2 #include<cmath> 3 #include<ctime> 4 #include<cstring> 5 #include<iostream> 6 #include<algorithm> 7 #include<queue> 8 #include<vector> 9 #define ll long long 10 using namespace std; 11 const int base=1<<10; 12 int k,siz[base],dig[base],ne[base][11]; 13 ll f[20][base][11]; 14 ll dfs(int w,int sta,bool o,bool pd) 15 { 16 if(!w) return siz[sta]==k; 17 if((!pd)&&(f[w][sta][k]!=-1)) return f[w][sta][k]; 18 ll up=pd?dig[w]:9,tmp(0); 19 for(int i=0;i<=up;++i) tmp+=dfs(w-1,(o&(!i))?0:ne[sta][i],o&(!i),pd&(i==up)); 20 if(!pd) f[w][sta][k]=tmp; 21 return tmp; 22 } 23 ll F(ll num) 24 { 25 int wei(0); 26 while(num){ 27 dig[++wei]=num%10; 28 num/=10; 29 } 30 return dfs(wei,0,1,1); 31 } 32 int fnd(int i,int num) 33 { 34 for(int j=num;j<10;++j) if(i&(1<<j)) return i^(1<<j)|(1<<num); 35 return i|(1<<num); 36 } 37 int main() 38 { 39 ll T,l,r; 40 memset(f,-1,sizeof(f)); 41 for(int i=0;i<base;++i){ 42 for(int j=0;j<10;++j){ 43 if(i&(1<<j)) siz[i]++; 44 ne[i][j]=fnd(i,j); 45 } 46 } 47 scanf("%d",&T); 48 for(int i=1;i<=T;++i){ 49 scanf("%lld%lld%d",&l,&r,&k); 50 printf("Case #%d: %lld\n",i,F(r)-F(l-1)); 51 } 52 return 0; 53 }