[HDU4352] XHXJ's LIS

先考虑LIS的nlogn解法

我们用dp[len]记录LIS长度为len时的最后一个数的大小,然后不断更新这些值,让每一个值都尽可能小

比如我现在的LIS是1 2 4 6,这时候下一个数是3,那么我们就要更新成1 2 3 6,让前面的数尽可能的小,这样就能让后面的数有更多的机会被加入

因为数字只有10个,我们可以状态压缩,记录每个数字是否出现在使前面的数尽量小的LIS中

然后转移的时候若在状态sta之后添加一个num,则找到第一个大于等于num的数,把它替换成num,若不存在比num大的数则直接添上num

这样既保证了前面的数尽可能的小,也没有改变LIS的长度

上面所说的转移可以预处理出来,next[i][j]表示原来状态为i,加上一个数j之后的状态

dp[dep][sta][K]记录长度为dep,LIS的状态是sta,LIS为K时的答案

注意前导0

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 typedef long long ll;
 4 int Next[1<<10][10],dig[20];
 5 ll dp[20][1<<10][10],cnt[1<<10];
 6 ll get_Next(int sta,int num){
 7     int pos=-1;
 8     for(int i=num;i<10;i++)
 9         if(sta&(1<<i)){ pos=i; break; }
10     if(pos==-1)sta|=(1<<num);
11     else sta=(sta^(1<<pos))|(1<<num);//
12     return sta;
13 }
14 void PP(){
15     memset(dp,-1,sizeof(dp));
16     for(int i=0;i<(1<<10);i++)
17         for(int j=0;j<10;j++){
18             if(i&(1<<j))cnt[i]++;
19             Next[i][j]=get_Next(i,j);
20         }
21 }
22 ll dfs(int dep,int sta,int K,int flag,int zero){
23     if(!dep)return cnt[sta]==K?1LL:0LL;
24     if(!flag&&dp[dep][sta][K]!=-1)return dp[dep][sta][K];
25     int lim=flag?dig[dep]:9;
26     ll ans=0;
27     for(int i=0;i<=lim;i++){
28         if(zero&&i==0)ans+=dfs(dep-1,0,K,flag&(i==lim),1);
29         else ans+=dfs(dep-1,Next[sta][i],K,flag&(i==lim),0);
30     }
31     if(!flag)dp[dep][sta][K]=ans;
32     return ans;
33 }
34 ll solve(ll x,ll K){
35     int dd=0;
36     while(x)dig[++dd]=x%10,x/=10;
37     return dfs(dd,0,K,1,1);//
38 }
39 int main(){
40     PP();
41     int T;
42     scanf("%d",&T);
43     ll A,B; int K;
44     for(int Case=1;Case<=T;Case++){
45         scanf("%lld%lld%d",&A,&B,&K);
46         printf("Case #%d: %lld
",Case,solve(B,K)-solve(A-1,K));
47     }
48     return 0;
49 }
View Code
原文地址:https://www.cnblogs.com/Ngshily/p/5483179.html