HDU - 4352 XHXJ's LIS (数位dp+状态压缩**)

题目:L到R,各位数字组成的严格上升子序列的长度为K的个数 (和LIS是一样不要求是连续的)

思路:用十位二进制表示0--9出现的情况,用和O(nlogn)的LIS一样的方法进行替换更新。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll A,B,dp[22][1<<10][11];//组数太多了,开第三维放k剪枝
int k,wei[22];

int getnow(int s,int x)//替换更新状态
{
    for(int i=x;i<=9;i++)
        if(s&(1<<i))
        return (s^(1<<i))|(1<<x);//找到第一个比x大的替换掉,1的个数不变
    return s|(1<<x);
}
int getnum(int s)//求s二进制中1的个数
{
    return __builtin_popcount(s);
}

ll dfs(int pos,int s,int limit,int lead)
{
    if(pos<1)
        return getnum(s)==k;
    if(!limit&&dp[pos][s][k]!=-1)
        return dp[pos][s][k];
    int up=limit?wei[pos]:9;
    ll ans=0;
    for(int i=0;i<=up;i++)
    {
        int now;
        if(lead&&i==0)
            now=s;
        else now=getnow(s,i);
        ans+=dfs(pos-1,now,limit&&i==up,lead&&i==0);
    }
    if(!limit) dp[pos][s][k]=ans;
    return ans;
}

ll solve(ll x)
{
    int len=0;
    while(x)
    {
        wei[++len]=x%10;
        x/=10;
    }
    ll ans=dfs(len,0,1,1);
    return ans;
}

int t,cas=0;
int main()
{
    //cout << "Hello world!" << endl;
    scanf("%d",&t);
    memset(dp,-1,sizeof dp);
    while(t--)
    {
        scanf("%lld%lld%d",&A,&B,&k);
        ll ans=solve(B)-solve(A-1);
        printf("Case #%d: %lld\n",++cas,ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/dllpxfire/article/details/81158940
今日推荐