• hdu4352 XHXJ's LIS[数位DP套状压DP+LIS$O(nlogn)$]


    统计$[L,R]$内LIS长度为$k$的数的个数,$Q le 10000,L,R < 2^{63}-1,k le 10$。


    首先肯定是数位DP。然后考虑怎么做这个dp。如果把$k$记录到状态里没有用。需要找到有效方法统一的表示前面填好的数的特点方便之后的填数。

    回顾LIS过程,当前数结尾的LIS是前面比他小的数的LIS中的max+1,但是没有办法记录下来,因为如果记录下前面以数字$0sim 9$结尾的maxLIS的话空间不够。

    尝试换一种表示方法。回顾LIS的$O(nlogn)$做法,发现维护了一个$g$数组表示长度$i$的LIS结尾最小$g[i]$,这个数组是单调增的。

    这是发现可以方便记录——用一个数$S$,把二进制第$g[i]$位$ ext{or}$到$S$上去。这样,$S$中是$1$的位置从小到大写下来就是$g$数组了。

    设$f(len,S,n,lead,limit)$,$S$是之前填好的数的$g$数组,$n$是前面的最长LIS长度。枚举当前位填啥,通过二分确定新的$S$,然后边界判一下是否$n=k$就好了。


    code细节:

    • 注意到$k$是相互独立的,可以分别进行统计,$f[k][...]$表示满足……条件(见上)之下、LIS长度为$k$的数的个数。
    • 注意判断前导0。
    • 加了一些剪枝,虽然并没有什么用。
     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<cmath>
     6 #include<vector>
     7 #define dbg(x) cerr << #x << " = " << x <<endl
     8 using namespace std;
     9 typedef long long ll;
    10 typedef double db;
    11 typedef pair<int,int> pii;
    12 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
    13 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
    14 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;}
    15 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;}
    16 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
    17 template<typename T>inline T read(T&x){
    18     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
    19     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
    20 }
    21 ll f[11][20][1<<10][2],b[20],tot;
    22 ll L,R;
    23 int T,k;
    24 ll dp(int len,int S,int n,int lead,int limit){
    25     if(!len)return n==k;
    26     if(n>k||n+len<k)return 0;
    27     if(!limit&&~f[k][len][S][lead])return f[k][len][S][lead];
    28     int num=limit?b[len]:9;ll ret=0;
    29     if(lead){
    30         ret+=dp(len-1,S,n,lead,limit&&!num);
    31         for(register int i=1;i<=num;++i)ret+=dp(len-1,1<<i,1,0,limit&&i==num);
    32     }
    33     else{
    34         int a[11]={0},m=0;
    35         for(register int i=0;i<=9;++i)if(S&(1<<i))a[++m]=i;
    36         for(register int i=0;i<=num;++i){
    37             int pos=lower_bound(a+1,a+m+1,i)-a;
    38             if(pos>m)ret+=dp(len-1,S|(1<<i),n+1,0,limit&&i==num);
    39             else ret+=dp(len-1,S^(1<<a[pos])|(1<<i),n,0,limit&&i==num);
    40         }
    41     }
    42     return limit?ret:f[k][len][S][lead]=ret;
    43 }
    44 inline ll solve(ll x){
    45     tot=0;while(x)b[++tot]=x%10,x/=10;
    46     return dp(tot,0,0,1,1);
    47 }
    48 
    49 int main(){//freopen("test.in","r",stdin);//freopen("test.ans","w",stdout);
    50     read(T);memset(f,-1,sizeof f);
    51     for(register int i=1;i<=T;++i)
    52         read(L),read(R),read(k),printf("Case #%d: %lld
    ",i,solve(R)-solve(L-1));
    53     return 0;
    54 }
    View Code

    总结:还是那句话,用有效的方法表示好填好的数的性质。

  • 相关阅读:
    ViewPager 滑动页(一)
    Fragment中Button的android:onClick 无法监听相应
    Button的四种Click响应方法
    环形图 自定义(一)
    Progress 自定义(一)-shape
    Button 自定义(一)-shape
    客户机页表遍历
    KVM的ept机制
    linux内核源码中两个重要的宏
    总结
  • 原文地址:https://www.cnblogs.com/saigyouji-yuyuko/p/11535869.html
Copyright © 2020-2023  润新知