HDU 5573 Binary Tree 2015上海现场赛B题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_19592437/article/details/50249483

题目大意:
有一个深度无限的完全二叉树,每个节点有一个标号:根节点标号为1,之后对于标号为x的节点,左孩子标号为2x,右孩子标号为2x+1(其实就是zkw线段树的标号规则)。现在从根节点开始向下走k步(根节点也算一步),每到一个节点可以选择加上或减去节点的标号,求最终结果为N的走法,其中N>=1且题目保证有解。

思路:
首先,我们用二进制数mask表示取数的方法,1表示+,0表示-,顺序从左向右,最右一位表示最后一次取数的方法;用pos表示最后到达节点的标号。这样就可以唯一确定一种走法,且很容易写出计算最终结果的函数f(mask,pos)。如k=3,mask=5,pos=7表示的路径为1 +,3 -,7 +,f(5,7)=5。
为方便表述再定义F(mask,x,pos,y)为mask右x位确定,pos左y位确定后,f的取值范围

经过观察很容易得到以下结论

  • 最后一个节点取法一定为+,即mask&1=1
  • 在k确定,后x步取法确定(即mask的右x位确定),前y步走法(即pos前y位确定)的情况下,f取值连续,且当所有未确定位全部置0时取得最小值

根据上述条件,只需依次确定mask和pos的值就可以得到答案

初状态mask最右位必然为1,pos最左位必然为1外其它位均未确定,根据题目限制N必包含在取值范围中,即F(1,1,1 << k,1)包含N

1.确定mask
从右向左依次确定mask,比如当前已确定前i位(mask其它位全部置0),则
F(mask,i,1<< k,1)最小值为f(mask,1<< k)
检查f(mask^(1<< i),1<< k))是否大于N,如果不大于N,则F(mask^(1<< i),i+1,1<< k,0)必包含N,否则F(mask,i+1,1<< k,0)必包含N

2.确定pos
此时mask已确定
从左向右依次确定pos,比如已确定前i位(pos其它位全部置0),则
F(mask,k,pos,i)最小值为f(mask,pos)
检查f(mask,pos^(1<<(k-1-i)))是否大于N,如果不大于N,则F(mask,k,pos^(1<<(k-1-i)),i+1)必包含N,否则F(mask,k,pos,i+1)必包含N

最后根据mask和pos打印路径即可
求解f(mask,pos)函数的复杂度为k,最终的复杂度O(k^2)

另外先pos再确定mask也是可行的

代码:
Problem : 5573 ( Binary Tree ) Judge Status : Accepted
RunId : 15788211 Language : G++ Author : qsqx

#include <iostream>
#include <stdio.h>
#include <queue>
#include <map>
#include <algorithm>
#include <string.h>
#define ll long long
#define forn(i,n) for(int i=0;i<(n);++i)
#define rep(i,n) for(int i=1;i<=(n);++i)
#define pii pair<int,int>
#define mp(x,y) make_pair((x),(y))
using namespace std;
ll f(ll mask,ll pos) {
    ll res = 0;
    while (pos) {
        if (mask & 1)
            res += pos;
        else
            res -= pos;
        pos >>= 1;
        mask >>= 1;
    }
    return res;
}
vector <pair<ll , char> >res;
void print(ll mask,ll pos) {
    res.clear();
    while (pos) {
        if (mask & 1)
            res.push_back(mp(pos, '+'));
        else
            res.push_back(mp(pos, '-'));
        pos >>= 1;
        mask >>= 1;
    }
    int len = res.size();
    for (int i = len - 1; i >= 0; --i) {
        printf("%lld %c\n", res[i].first, res[i].second);
    }
}
void solve() {
    ll n;
    int k;
    cin >> n >> k;
    ll mask = 1;
    for (int t = 1; t <k;++t) {
        if (f(mask | (1LL << t), 1LL << (k - 1))<=n) {
            mask |= (1LL << t);
        }
    }
    ll pos = 1LL << (k - 1);
    for (int t = k - 2; t >= 0; --t) {
        if (f(mask, pos | (1LL << t)) <= n)
            pos |= (1LL << t);
    }

    print(mask, pos);
}
int main(){
    //freopen("abc.txt", "r", stdin);
    int T;
    cin >> T;
    rep(t, T) {
        printf("Case #%d:\n",t);
        solve();
    }
    return 0;
}
阅读更多

没有更多推荐了,返回首页