[数位dp+二分] LightOJ 1105 - Fi Binary Number

来源:互联网 发布:linux配置samba服务器 编辑:程序博客网 时间:2024/06/01 08:09

LightOJ 1105 - Fi Binary Number

题意:定义Fi-Binary Number,只含有0和1,不含前导0和连续的1。按字典序,前几个数为1,10,100,101,1000,1001。求第n个FBnumber。

题解:通过数位dp可以快速求出0~x中有多少个FBnumber,然后二分求出最小的x就行了。

#include<bits/stdc++.h>#define ll long long intusing namespace std;const ll inf = ~0ull>>1;ll dp[70][3];int num[70];ll dfs(int pos, int st, bool f){ // st为0表示前一位是0,为1表示前一位是1,为2表示含有连续的1,返回st==2得到不是FB数的个数    if(pos < 1) return st == 2;    if(!f && dp[pos][st] != -1) return dp[pos][st];    int end = f? num[pos] : 1;    ll res = 0;    for(int i = 0; i <= end; ++i){        if(st == 2 || (st == 1 && i == 1)) res += dfs(pos-1, 2, f && i == end);        else if(i == 1) res += dfs(pos-1, 1, f && i == end);        else res += dfs(pos-1, 0, f && i == end);    }    if(!f) dp[pos][st] = res;    return res;}ll solve(ll x){    ll res = 1, tmp = x;    int len = 0;    while(x){        num[++len] = x&1;        x >>= 1;    }    res = tmp - dfs(len, 0, 1);    return res;}void print(ll n){    int len = 0;    while(n){        num[++len] = n&1;        n >>= 1;    }    while(len) printf("%d", num[len--]);}int main(){    int T, ca = 1;    scanf("%d", &T);    memset(dp, -1, sizeof(dp));    while(T--){        ll n;        scanf("%lld", &n);        ll l = 0, r = inf, ans = 0;        while(l <= r){            ll mid = (l+r) >> 1;            if(solve(mid) >= n) {                ans = mid;                r = mid-1;            }            else l = mid+1;        }        printf("Case %d: ", ca++);        print(ans);        printf("\n");    }}


0 0
原创粉丝点击