牛客挑战赛51 C.NIT的数(数位dp)
Posted issue是fw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了牛客挑战赛51 C.NIT的数(数位dp)相关的知识,希望对你有一定的参考价值。
题意
给你一个正整数 x x x,求第 k k k 小的正整数 y y y 满足 x ≤ y x \\leq y x≤y 且 y y y 是一个回文数。
写出一个能求出 [ 0 , x ] [0,x] [0,x]有多少回文数的函数就可以使用二分查找来解决问题
那么对于确定的 x x x,设有 r r r位数字
那么 [ 1 , r − 1 ] [1,r-1] [1,r−1]位的数字一定都小于 x x x,于是可以直接组合数算出方案
至于那些位数是 r r r的数字,可以用数位 d p dp dp
因为算的是回文数,于是只需要从高位往低位枚举一半就可以直到是否满足条件
如果在 [ r / 2 + 1 , r ] [r/2+1,r] [r/2+1,r]已经分出大小关系就直接返回结果,否则需要知道 [ 1 , r / 2 ] [1,r/2] [1,r/2]的第一个最高位不相等的是比 x x x更大还是更小
定义 f [ i ] [ j ] [ q ] [ w ] f[i][j][q][w] f[i][j][q][w]表示从高位往低位枚举到第 i i i位, [ 1 , r / 2 ] [1,r/2] [1,r/2]中比 x x x大的最高位是第 j j j位,比 x x x小的最高位是第 q q q位
当前 [ i , r ] [i,r] [i,r]填数字的限制情况为 w w w
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e6+10;
#define int long long
int f[22][20][20][2],half,a[20];
int dfs(int len,int pre,int q,int w,int limit)
{
if( f[len][q][w][limit]!=-1 ) return f[len][q][w][limit];
if( len<=half )
{
if( !limit || w>=q ) return f[len][q][w][limit] = 1;
else return f[len][q][w][limit] = 0;
}
int las = limit?a[len]:9, ans = 0;
for(int i=0;i<=las;i++)
{
if( pre==1 && i==0 ) continue;//第一位不能为0
int qq = q, ww = w;
if( i>a[pre] ) qq = pre;
if( i<a[pre] ) ww = pre;
ans += dfs( len-1,pre+1,qq,ww,limit&(i==las) );
}
return f[len][q][w][limit] = ans;
}
int quick(int x,int n)
{
int ans = 1;
for( ; n ; n>>=1,x=x*x )
if( n&1 ) ans = ans*x;
return ans;
}
int solve(int x)
{
if( x==0 ) return 0;
memset( f,-1,sizeof f );
a[0] = 0;
while( x ) a[++a[0]] = x%10, x/=10;
int ans = 0;
for(int i=1;i<a[0];i++)
{
half = i/2;
if( i==1 ) ans = 9;
else
{
int temp = 9*quick(10,half-1);
if( i&1 ) temp = temp*10;
ans += temp;
}
}
half = a[0]/2;
return ans+dfs(a[0],1,0,0,1);
}
signed main()
{
int x,k; cin >> x >> k;
int sum = solve( x-1 )+k;
int l = x, r = 1e18, ans = 0;
while( r>=l )
{
int mid = l+r>>1;
if( solve(mid)<sum ) l = mid+1;
else r = mid-1, ans = mid;
}
cout << ans;
}
以上是关于牛客挑战赛51 C.NIT的数(数位dp)的主要内容,如果未能解决你的问题,请参考以下文章