HDU 4352 XHXJ's LIS

XHXJ's LIS

题意:求出给定区间[L,R]  (0<L<=R<263-1 and 1<=K<=10) 满足LIS(非连续严格递增子序列)为K的个数?

如区间[123,321]中的124的LIS为3;

<1> 总结数位DP的优化方式:

  • 归纳高位之间的相同点,就是状态的转化是关键;

例: 1.在codeforces 的beautiful number 中,要求满足能被自己的每一个非零数字整除的数的个数。这时构造出lcm,当高位mod(所有元素的lcm直接优化到2520)后将会出现很多重叠的子问题,这就是状态的转移;当然里面各种的lcm只需存储即可;

     2.本题:首先要知道给定一个序列,知道怎么求解LIS,如 1 2 4 6 ,当加入3时,这时长度为3的LIS的最小值为3,而不是4.(使用数组存储的是[长度] = minval),但是本题并不是这样的LIS,这需要模糊看待才能将不一样的高位看成是相同的,才能有重叠的子结构.

在dfs开始时,state为0,这时如果前面加入的是98之后加入1,那么按照原始的LIS长度是为1的,但是这里长度看成3,为什么?因为这里state存储的只是状态,就是说dfs到pos-1位时,只知道前面有哪些值,并不需要知道加入的前后顺序~~那这就将排序问题简化为了组合问题,这才是优化的本质。这也是为什么说state中的1个数就是LIS的值;(故意按照递增排列的)

但是还有问题:

  • 能不能直接加入一个数,而不将前面的状态删减?不能,因为当你只是添加该位置的数时,就会发现在最后判断是否state的1的个数为k时,只能是出现数字为k的值。如在样例中你只能是 221,133,144这样的值,而198这样的值不行。所以必须要删减。
  • 删减的个数?题解中是只删一个,如果要将当前插入的值放到最后,是需要将将后面更大的值全部删掉的,但是这是一个组合问题,只需要优化该数所在的数值即可。即如果前面的数为1 2 4 5 现在插入的是3,3是LIS长度为3的点,之前该点的值是4,就把4改成3.开始我想的是把最大值删去,在把当前的值插入是不是这样的优化更好呢?但是发现里面少了很多值.(你改后把样例输入发现结果为125,少计算了如191,190,291,290,292这些数);
  • 是否能前置0?1.当state还是0时,是不能将0加入状态,但是当state中已经加入的元素,就可以;对情况1,这时就是数值的前置0,加了会和后面同值的数重复,对于情况2,这时看做为前面LIS的优化相同长度的值,可以。

到此,就可以确定dfs的参数为4个pos,state,edge(原来就有),还要考虑一个是否有前置0的zero参数。

(使用的是内置函数__builtin_popcount()来计算数位1的个数)

#include<bits/stdc++.h>
using namespace std;
#define rep(i,n) for(int (i)= 0;i < (n);i++)
#define MS1(a) memset(a,-1,sizeof(a))
typedef long long ll;
ll dp[20][1<<10][11];
int bit[20],K;
int newstate(int state,int v)
{
    for(int i = v;i < 10;i++)
        if(state &(1 << i)) return (state ^ (1 << i))|(1<<v);
    return state|(1<<v);
}
ll dfs(int pos,int state,bool edge,bool zero)
{
    if(pos == -1) return __builtin_popcount(state) == K;
    if(!edge && dp[pos][state][K] != -1) return dp[pos][state][K];
    int end = edge ? bit[pos]:9;
    ll ans = 0;
    for(int i = 0;i <= end;i++){
        ans += dfs(pos - 1,(zero && i == 0)?0:newstate(state,i),edge && i == end,zero && i == 0);
    }
    if(!edge) dp[pos][state][K] = ans;
    return ans;
}
ll calc(ll x)
{
    int tot = 0;
    while(x){
        bit[tot++] = x%10;
        x /= 10;
    }
    return dfs(tot - 1,0,1,1);
}
int main()
{
    int T,kase = 1;
    MS1(dp);
    cin>>T;
    while(T--){
        ll L,R;
        scanf("%I64d%I64d%d",&L,&R,&K);
        printf("Case #%d: %I64d\n",kase++,calc(R) - calc(L-1));
    }
}
View Code

 

你可能感兴趣的:(HDU 4352 XHXJ's LIS)