poj3208 Apocalypse Someday 數位dp+二分 求第K(K <= 5*107)個有連續3個6的數。

/**
題目:poj3208 Apocalypse Someday
連接:http://poj.org/problem?id=3208
題意:求第K(K <= 5*107)個有連續3個6的數。
思路:數位dp+二分。
dp[i][j]表示長度爲i,前綴狀態爲j時含有的個數。
j=0表示含有前導0;
j=1表示前綴連續1個6
j=2表示前綴連續2個6
j=3表示前綴連續3個6
j=4表示前綴不是6;


*/

//#include<bits/stdc++.h>
#include<cstring>
#include<cstdio>
#include<iostream>
#include<map>
#include<algorithm>
#include<queue>
using namespace std;
#define P pair<int,int>
#define ms(x,y) memset(x,y,sizeof x)
#define LL long long
const int maxn = 1005;
const int mod = 1e9+7;
const int maxnode = 100000*6+10;
const int sigma_size = 26;
const LL inf = 1e18;
int digit[22];
LL dp[22][5];///0表示前導0,1表示前綴一個6,2表示前綴2個6,3表示前綴3個6,4表示前綴不是6;
int next_state(int state,int x)
{
    if(state==3) return 3;
    if(x==6){
        if(state==1||state==2) return state+1;
        else return 1;
    }else
    {
        return 4;
    }
}
LL dfs(int len,int state,int bounded)
{
    if(len==0){
        return state==3;
    }
    if(!bounded&&dp[len][state]!=-1) return dp[len][state];
    int d = bounded?digit[len]:9;
    LL ans = 0;
    for(int i = 0; i <= d; i++){
        if(state==0){
            if(i==0) ans += dfs(len-1,0,bounded&&(i==d));
            else ans += dfs(len-1,i==6?1:4,bounded&&(i==d));
        }else
        {
            ans += dfs(len-1,next_state(state,i),bounded&&(i==d));
        }
    }
    if(!bounded){
        dp[len][state] = ans;
    }
    return ans;
}
LL solve(LL n)
{
    int len = 0;
    while(n){
        digit[++len] = n%10;
        n /= 10;
    }
    return dfs(len,0,true);
}
int main()
{
    int T;
    ms(dp,-1);
    cin>>T;
    while(T--)
    {
        int n;
        scanf("%d",&n);
        LL lo = 1, hi = inf, mi;
        while(lo<hi){
            mi = (lo+hi)/2;
            LL ans = solve(mi);
            if(ans>=n){///找下界。
                hi = mi;
            }else
            {
                lo = mi+1;
            }
        }
        printf("%lld\n",hi);
    }
    return 0;
}

/*

*/
相關文章
相關標籤/搜索