题目: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;
}