D 明七暗七 +数位dp+二分
转变一下题意可以变为求区间内所有满足含有7或者是7的倍数的数,第一个条件数位dp容易枚举,第二个条件需要记录好每位数对7取模的余数,如果余数为0即为倍数。如14,第一位数位10,对7取模的余数维3,第二位为4对7取模余数为4,两者之和对7取模余数为0,故为7的倍数。故可以直接数位dp求出0 - l区间满足的数。左端点为m,我们只二分需要找到一个右端点是得满足刚好为n个时即可。
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5+5;
int dp[20][10][2];//第一维维当前为第几位数,第二维为每个位对7取模的余数之和,第三维判断是否含7
int a[20];
int Pow(int x,int y){
int tot = 1;
while(y){
if(y&1) tot = tot*x;
x = x*x;
y>>=1;
}
return tot;
}
int dfs(int pos,int limit,int sum,int flag){
if(pos==-1){
if(flag ||sum%7==0){//满足含7或者是7的倍数即可
return 1;
}
else return 0;
}
if(!limit && dp[pos][sum][flag]!=-1) return dp[pos][sum][flag];
int up = limit?a[pos]:9;
int ans = 0;
for(int i = 0;i<=up;i++){
int sum_ = (sum+(i*Pow(10,pos))%7)%7;//某位数对7取模的余数+前几位的余数
int flag_ = flag|(i==7);
// int num_ = num*10+i;
ans+=dfs(pos-1,limit && up==i,sum_,flag_);
}
if(!limit) dp[pos][sum][flag] = ans;
return ans;
}
int solve(int x){
int pos = 0;
while(x){
a[pos] = x%10;
x/=10;
pos++;
}
return dfs(pos-1,1,0,0);
}
signed main(){
cin>>m>>n;
memset(dp,-1,sizeof(dp));
int ans = solve(m);
int l = m+1;
int r = 4e12;//枚举最大右端点
int p =0;
while(l<=r){
int mid = l+r>>1;
int num = solve(mid);
if(num-ans>=n){
p = mid;
r = mid-1;
}else{
l = mid+1;
}
}
cout<<p<<endl;
return 0;
}