【hdu4352】XHXJ's LIS

【题意】

将一个数x当成一个数串,对其做最长上升子序列。记结果为 a x a_x ax。对一个区间 [ l , r ] [l,r] [l,r],求有多少个数 x x x,使 a x = k a_x=k ax=k

【分析】

先提一提LIS的 O ( n l o g n ) O(nlogn) O(nlogn)的做法:
维护一个数列 t a i l [ i ] tail[i] tail[i],表示长度为i的LIS的最后一个元素最小的数。当新加入一个数 a [ i ] a[i] a[i]时,对 t a i l tail tail二分。若有一个位置,使 t a i l [ m i d ] &lt; a [ i ] &lt; t a i l [ m i d + 1 ] tail[mid]&lt;a[i]&lt;tail[mid+1] tail[mid]<a[i]<tail[mid+1],给 t a i l [ m i d + 1 ] tail[mid+1] tail[mid+1]赋值为 a [ i ] a[i] a[i]。否则,在 t a i l tail tail的最末尾将 a [ i ] a[i] a[i]插入。答案即为 t a i l tail tail所含元素的数量。
然后想到:设 f ( i , S ) f(i,S) f(i,S)表示当前做到第 i i i位, t a i l tail tail的状态为S。然后按题意转移即可。
但有一个小问题:如何表示S?若直接将 t a i l tail tail塞进状态,你不MLE谁MLE。
由于 t a i l tail tail数组单调递增,故 0   9 0~9 0 9在数组中只会出现一次。所以,不妨把 S S S改造成二进制,第 i i i位表示 i i i是否在 t a i l tail tail数组里面。然后每新加入一个数 x x x,找到一个等于 1 1 1,但位数比它高的位 s s s,将 s s s位置 0 0 0,再将 x x x位置 1 1 1。若 x x x为当前最高位,直接将 x x x位置 1 1 1即可。
这个过程可以用位运算实现。具体见代码。

【代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int mn = 20;
int d[mn], n;
ll f[mn][1 << 10][11];
inline int lenth(int S)
{
    int ret = 0;
    while(S)
        ret += (S & 1), S >>= 1;
    return ret;
}
inline int push(int S, int x)
{
    for(int i = x; i < 10; i++)
        if(S & (1 << i))
            return (S ^ (1 << i)) | (1 << x);
    return S | (1 << x);
}
inline ll dp(int pos, int S, bool f0, bool lim)
{
    if(!pos)
        return lenth(S) == n;
    if(!lim && ~f[pos][S][n])
        return f[pos][S][n];
    int m = lim ? d[pos] : 9;
    ll ans = 0;
    for(int i = 0; i <= m; i++)
        ans += dp(pos - 1, (f0 && i == 0) ? 0 : push(S, i), f0 && i == 0, lim && i == m);
    if(!lim)
        f[pos][S][n] = ans;
    return ans;
}
inline ll calc(ll num)
{
    int cnt = 0;
    while(num)
        d[++cnt] = num % 10, num /= 10;
    return dp(cnt, 0, 1, 1);
}
int main()
{
    int t;
    ll l, r;
    scanf("%d", &t), memset(f, -1, sizeof f);
    for(int i = 1; i <= t; i++)
        scanf("%lld%lld %d", &l, &r, &n), printf("Case #%d: %lld\n", i, calc(r) - calc(l - 1));
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值