题意:给出l,r和x,y,要求找出l道r之间含有x个4,y个7的第k大的数。
数位DP,先预处理一下,处理处当前是i位数,含有x个4,y个7的数的个数,然后从高位到低位枚举数字就可以了。
然后二分查找。。代码有注释
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
ll dp[21][21][21];
int dig[21];
int x,y;
ll l,r;
void init()
{
int i,j,k;
memset(dp,0,sizeof(dp));
dp[0][0][0]=1;
for(i=1;i<=20;i++)
{
for(j=0;j<i;j++)
{
for(k=0;k+j<i;k++)
{
dp[i][j+1][k]+=dp[i-1][j][k];
dp[i][j][k+1]+=dp[i-1][j][k];
dp[i][j][k]+=8*dp[i-1][j][k]; //除去4,7还有8个数
}
}
}
}
ll get_count(ll num)
{
int i,j;
int len=0;
while(num)
{
dig[++len]=num%10;
num/=10;
}
ll ans=0;
int cx=x,cy=y;
for(i=len;i>0;i--)
{
for(j=0;j<dig[i];j++)
{
if(j==4) //枚举当前位的数字,加上下一位满足的个数
{
if(cx)
ans+=dp[i-1][cx-1][cy];
}
else if(j==7)
{
if(cy)
ans+=dp[i-1][cx][cy-1];
}
else
ans+=dp[i-1][cx][cy];
}
if(dig[i]==4)
cx--;
if(dig[i]==7)
cy--;
if(cx<0||cy<0)
break;
}
return ans;
}
ll solve(ll k) //二分,这个k是l+需要找第k大的数
{
ll left=l,right=r,mid,ans=-1;
while(left<=right)
{
mid=(left+right)>>1;
if(get_count(mid+1)>=k) //因为题目要求不含边界
{
ans=mid;
right=mid-1;
}
else
left=mid+1;
}
return ans;
}
int main()
{
int t,flag=1;
scanf("%d",&t);
while(t--)
{
scanf("%I64d%I64d%d%d",&l,&r,&x,&y);
init();
int n;
scanf("%d",&n);
ll a=get_count(l+1);
ll b=get_count(r+1);
printf("Case #%d:\n",flag++);
while(n--)
{
ll k;
scanf("%I64d",&k);
if(k>b-a)
{
printf("Nya!\n");
continue;
}
printf("%I64d\n",solve(a+k)); //这里是a+k
}
}
return 0;
}