题意:
A数列为,包含7或者能被7整除的数从小到大构成。
{a[1]=7,a[2]=14,a[3]=17,a[4]=21,a[5]=27,a[6]=28,a[7]=35,a[8]=37,a[9]=42,a[10]=47};
B数列为,是A数列里的数,但不包含第a[i]个A数列里的数列。
也就是B数列要去掉 a[7],a[14],a[17]这些要去掉。
{b[1]=7,b[2]=14,b[3]=17,b[4]=21,b[5]=27,b[6]=28,b[7]=37,b[8]=42,b[9]=47,b[10]=49};
现在给N,求第N个B数列里的数。
思路:
首先值得肯定的是,我们必须知道A数列,所以数位dp关于A数列。这个都很简单。
dp[i][j][k] 第i位,余数是j,k 代表是否含有了7.
然后就看第N个B数列的数,实际是第几个A数列的数。
我们假设是第N个B 数列的数,是第X个A数列的数。
那么满足N=X-solve(X) solve(X) 代表1~X中有几个A数列的数。
会发现X-solve(X) 随着X的增大而增大。
所以二分出最小满足X-solve(X)=N 的X。
接着再一个二分求第X个A数列的数是多少就可以了~
最后就是需要注意,题目要求范围是2^63-1,所以开成 unsigned long long。
代码:
#include"stdio.h" #include"algorithm" #include"string.h" #include"map" #include"iostream" #include"queue" #include"string" #define ll unsigned long long using namespace std; ll dp[22][8][2]; int num[22]; ll dfs(int site,int s,int o,int f) { if(site==0) { if(o==1) return 1; if(s==0) return 1; return 0; } if(!f && dp[site][s][o]!=-1) return dp[site][s][o]; ll ans=0; int len=f?num[site]:9; for(int i=0; i<=len; i++) { if(o==1) ans+=dfs(site-1,(s*10+i)%7,1,f&&i==len); else { if(i==7) ans+=dfs(site-1,(s*10+i)%7,1,f&&i==len); else ans+=dfs(site-1,(s*10+i)%7,0,f&&i==len); } } if(!f) dp[site][s][o]=ans; return ans; } ll solve(ll x) { int cnt=0; while(x) { num[++cnt]=x%10; x/=10; } return dfs(cnt,0,0,1)-1; } int main() { ll n; memset(dp,-1,sizeof(dp)); while(scanf("%llu",&n)!=-1) { ll l=1,r=(1LL<<63)-1,mid,kx; while(l<=r) { mid=(l+r)/2; ll tep=mid-solve(mid); if(tep>=n) { kx=mid; r=mid-1; } else l=mid+1; } ll ans; l=1,r=(1LL<<63)-1; while(l<=r) { mid=(l+r)/2; if(solve(mid)>=kx) { ans=mid; r=mid-1; } else l=mid+1; } printf("%llu\n",ans); } return 0; }
时间: 2024-11-19 12:37:42