P9689 Bina. 题解

P9689

难评。

可以直接枚举树的高度,看有没有砍掉 $m$ 个点。

当 $k$ 小于树的深度时,显然节点个数为 $2^k-1$,此时编号和是容易的。

但是当 $k$ 等于树的深度时,编号和不能直接求出,因为最后一层的编号不是连续的。

令 $x=ed-st+1$。子树大小是容易通过记忆化得到的。考虑编号和,容易发现当 $x$ 是偶数时,两边的子树的形态是一致的,考虑从这里下手。

不妨设 $f_x$ 表示传入的参数为 $x$ 时,且该子树根节点为 $1$ 的编号和。

但是转移时变为了以 $2$ 为根的子树和以 $3$ 为根的子树的编号和加 $1$。

然后看它与 $1$ 为子树的编号和的变化。以 $2$ 为例,发现就是如果有 $c$ 个节点,满足 $2^p-1<c\le2^{p+1}-1$,则变化了 $\sum\limits_{i=0}^{p}4^i+2^{p+1}(c-2^p+1)$,即按层考虑。

注意 long long,赛时卡了好久。

和线段树是很像的,每层是不会超过两次查询的,然后每次合并 $sz_{ls}$ 和 $sz_{rs}$ 是 $\mathcal{O}(\log n)$。可以使用 umap 存储 $f$。

时间复杂度:$\mathcal{O}(T\log^2n)$。

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

ll n,m;
unordered_map<ll,ll> mp,f;

ll dfs(ll x){
    if(mp[x])return mp[x];
    if(x==1)return 1;
    if(x%2==0)return mp[x]=2*dfs(x/2)+1;
    return mp[x]=dfs(x/2)+dfs(x/2+1)+1;
}
ll calc(ll l,ll r){
    return (l+r)*1ll*(r-l+1)/2;
}
ll work(ll x){
    ll res=0;
    for(int i=1;i<=31;i++)if((1ll<<i)-1<x)res+=(1ll<<(2*i-2));else{
        res+=(1ll<<(i-1))*(x-((1<<(i-1)))+1);
        break;
    }
    return res;
}
ll gh(ll x){
    if(f[x])return f[x];
    if(x==1)return 1;
    int p=dfs(x/2);
    if(x%2==0){
        f[x]=2*gh(x/2)+work(p)+2*work(p)+1;
        return f[x];
    }
    f[x]=gh(x/2+1)+gh(x/2)+2*work(p)+work(dfs(x/2+1))+1;
    return f[x];
}
void solve(){
    cin>>n>>m;
    ll al=dfs(n);ll ans=0;
    if(m>=al){
        cout<<"-1"<<"\n";
        return;
    }
    for(int i=1;i<=31;i++)
        if((1ll<<i)-1<al){
            if((al-(1ll<<i)+1)>=m)ans=max(ans,calc(1,(1ll<<i)-1)/i);
        }else{
            if(!m)ans=max(ans,gh(n)/i);
            break;
        }
    cout<<ans<<"\n";
}

int main(){
    int T;cin>>T;
    while(T--)solve();
    return 0;
}
posted @ 2023-10-02 22:45  Pengzt  阅读(4)  评论(0编辑  收藏  举报  来源