1. 写在题前
- 自己第一次学习数位dp,感觉还蛮有意思
- 接下来几篇文章大概都是dp吧
2. 题意
- 定义了一种叫做Nya的数,如果这种数有x个4和y个7,那么他就叫Nya数,给定一个区间(p,q],问这个区间内第k大的数是什么
3. 关于数位dp
- 常用于构造满足条件的数,该条件与该数的每一位有关,同时存在转移方程,常用dp去描述某个范围内这种数的个数等
4. 关于本题
-
dp[i][j][k]
表示长度小于等于i
的数中,有j
个4和k
个7的数的个数
dp[i][j][k] += dp[i-1][j][k]*8//01235689
dp[i][j][k] += dp[i-1][j-1][k] //4
dp[i][j][k] += dp[i-1][j][k-1] //7
dp[0][0][0] = 1
- 预处理完dp数组之后可以求得给定上限之后这个范围内的nya数的个数
- 理由二分查找区间第k大
5. 代码
#include<iostream>
#include<string.h>
using namespace std;
#define clr(x) memset(x,0,sizeof(x))
typedef long long ll;
ll dp[22][22][22];
ll p,q,x,y,n;
int T;
int cnt,bit[22];
ll fnd(ll n)//查找1~n中的nya数的个数
{
cnt = 0;
while(n)
{
bit[++cnt] = n%10;
n/=10;
}
ll m1 = x;
ll m2 = y;
ll ans = 0;
for(int i = cnt;i>=1;i--)
{
if(bit[i]<=4)ans+=bit[i]*dp[i-1][m1][m2];
else if(bit[i]<=7&&bit[i]>4)
{
ans += (bit[i]-1)*dp[i-1][m1][m2];
if(m1)ans += dp[i-1][m1-1][m2];
}
else if(bit[i]>7)
{
ans += (bit[i]-2)*dp[i-1][m1][m2];
if(m1)ans += dp[i-1][m1-1][m2];
if(m2)ans += dp[i-1][m1][m2-1];
}
if(bit[i] == 4)m1--;
else if(bit[i] == 7)m2--;
if(m1<0||m2<0)return ans;
}
if(m1 == 0&&m2 == 0)ans++;
return ans;
}
void init()
{
clr(dp);
dp[0][0][0] = 1;
for(int i = 1;i<22;i++)
{
for(int j = 0;j<=20;j++)
{
for(int k = 0;k<=20;k++)
{
dp[i][j][k] += dp[i-1][j][k]*8;
if(j)dp[i][j][k] += dp[i-1][j-1][k];
if(k)dp[i][j][k] += dp[i-1][j][k-1];
}
}
}
}
int main()
{
init();
cin>>T;
for(int tt = 1;tt<=T;tt++)
{
printf ("Case #%d:\n", tt);
cin>>p>>q>>x>>y;
ll tep = fnd(p);
int qn;cin>>qn;
while(qn--)
{
cin>>n;
ll l = p;
ll r = q;
while(r-l>1)
{
ll mid = (l+r)>>1;
ll pos = fnd(mid);
if(pos-tep>=n)r = mid;
else l = mid;
//cout<<"fuck"<<endl;
}
if(fnd(l)-tep>=n)cout<<l<<endl;
else if(fnd(r)-tep>=n)cout<<r<<endl;
else cout<<"Nya!"<<endl;
}
}
}
网友评论