WUST OJ 1270:LIS(数位DP)

1270: LIS
Time Limit: 1 Sec
Memory Limit: 128 MB
64bit IO Format: %lld

Description
给三个整数L,R,K。求闭区间[L,R]内有多少个数A,使得如果把A当成字符串时,它的最长严格递增子序列长度为K,比如12324的最长严格递增子序列长度为4。

Input
多组测试样例。
每组含三个整数L,R,K.占一行。 ( 0 &lt; L &lt; = R &lt; 2 63 − 1 , 1 = &lt; K &lt; = 10 ) . (0&lt;L&lt;=R&lt;2^{63}-1,1=&lt;K&lt;=10). 0<L<=R<2631,1=<K<=10.

Output
对于每组测试样例输出一个整数,表示满足要求的数的个数,占一行。

Sample Input
123 321 2

Sample Output
139

思路: d [ i ] [ j ] [ k ] d[i][j][k] d[i][j][k]表示低 i i i中,第 i i i位的数字为 j j j,且最长上升子序列构成的状态为k的数的个数。

当更新到第 i + 1 i+1 i+1位时,可以采用最长上升子序列 n l o g n nlog n nlogn解法的思想,用当前选中的这个数替代状态 k k k中第一个大于等于它的数。

#include<bits/stdc++.h>
using namespace std;
const int MAX=1e6+10;
const int MOD=1e9+7;
const int INF=1e9+7;
const double PI=acos(-1.0);
typedef long long ll;
ll p[20];
ll d[20][10][1<<10];
ll A[20][10][12];
ll low(int x,int y){for(;x>=0;x--)if((1<<x)&y)return 1<<x;return 0;}
ll big(int x,int y){for(;x<=9;x++)if((1<<x)&y)return 1<<x;return 0;}
ll check(int x,int y,int len)
{
    int t=0,q=y,ans=0;
    ans=max(ans,__builtin_popcount(x));
    ans=max(ans,__builtin_popcount(y));
    for(int i=0;(1<<i)<=x;i++)
    {
        q-=(1<<i)&y;
        t+=(1<<i)&x;
        ans=max(ans,__builtin_popcount(t+q));
    }
    return ans==len;
}
ll cal(ll x,int len)
{
    if(x==0)return 0;
    int n=0;
    while(x)p[n++]=x%10,x/=10;n--;
    ll ans=0;
    for(int i=0;i<n;i++)
    for(int j=1;j<=9;j++)ans+=A[i][j][len];//因为我不是用搜索写的,所以没有这个预处理会T
    int S=0;
    for(int i=n;i>=0;i--)
    {
        if(i==n)for(int j=1;j<p[i];j++)ans+=A[i][j][len];
        else
        {
            for(int j=0;j<p[i];j++)
            for(int k=1;k<(1<<10);k++)ans+=check(S,k,len)*d[i][j][k];
        }
        S+=(1<<p[i])-big(p[i],S);
    }
    return ans+(__builtin_popcount(S)==len);
}
int main()
{
    memset(d,0,sizeof d);
    memset(A,0,sizeof A);
    for(int i=0;i<=19;i++)
    {
        if(i==0)
        {
            for(int j=0;j<=9;j++)d[i][j][1<<j]=1;
            continue;
        }
        for(int j=0;j<=9;j++)
        for(int k=0;k<=9;k++)
        for(int S=1;S<(1<<10);S++)
        {
            if(d[i-1][k][S]==0)continue;
            d[i][j][S+(1<<j)-low(j,S)]+=d[i-1][k][S];
        }
        for(int j=0;j<=9;j++)
        for(int S=1;S<(1<<10);S++)A[i][j][__builtin_popcount(S)]+=d[i][j][S];
    }
    ll L,R,K;
    while(scanf("%lld%lld%lld",&L,&R,&K)!=EOF)printf("%lld\n",cal(R,K)-cal(L-1,K));
    return 0;
}

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值