Live2D

Solution -「Gym 102798K」Tree Tweaking

Description

  Link.

  给定排列 {pn},求任意重排 pl..r 的元素后,将 {pn} 依次插入二叉搜索树时结点深度之和的最小值。

  n105rl+1200

Solution

  先把不作修改的二叉搜索树建出来——按值升序遍历,单调栈维护即可,这就相当于建 (pi,i) 的笛卡尔树。考虑此时树上一个“可修改连通块”的性质:它的“不可修改子树”的父亲和子树大小是一定的,无论这棵子树内部如何作修改。这提示我们可以独立地考虑每个“可修改连通块”。首先遍历得到连通块邻接的子树大小(若有空儿子,增加一个大小为 0 的子树,用于占位),得到序列 a1..k,则在其上 DP,令 f(l,r) 表示将 al..r 建出二叉搜索树的最小深度和,则:

f(l,r)=(rl+i[l,r]ai)+minp[l,r){f(l,p)+f(p+1,r)}.

所以 O((rl+1)3) 求出所有 f,求和就能得到答案。复杂度 O(n+(rl+1)3)

Code

/*~Rainybunny~*/

#include <cstdio>

#define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )

typedef long long LL;

inline void chkmin( LL& a, const LL b ) { b < a && ( a = b ); }

const int MAXN = 1e5, MAXK = 200;
int n, a[MAXN + 5], b[MAXN + 5], L, R;
int top, stk[MAXN + 5], ch[MAXN + 5][2], siz[MAXN + 5];
int idx, val[MAXK + 5];
LL f[MAXK + 5][MAXK + 5], sum[MAXK + 5];

inline void collect( const int u ) {
    if ( !u || b[u] > R ) return void( val[++idx] = siz[u] );
    collect( ch[u][0] ), collect( ch[u][1] );
}

inline LL solve( const int u ) {
    idx = 0, collect( u );
    rep ( i, 1, idx ) sum[i] = sum[i - 1] + val[i];
    rep ( len, 2, idx ) {
        for ( int l = 1, r; ( r = l + len - 1 ) <= idx; ++l ) {
            LL& cur = f[l][r] = 1ll << 60;
            rep ( k, l, r - 1 ) {
                chkmin( cur, f[l][k] + f[k + 1][r] );
            }
            cur += sum[r] - sum[l - 1] + r - l;
        }
    }
    return f[1][idx];
}

int main() {
    scanf( "%d", &n );
    rep ( i, 1, n ) scanf( "%d", &a[i] ), b[a[i]] = i;
    scanf( "%d %d", &L, &R );

    rep ( i, 1, n ) {
        while ( top && b[i] < b[stk[top]] ) ch[i][0] = stk[top--];
        if ( top ) ch[stk[top]][1] = i;
        stk[++top] = i;
    }

    per ( i, n, 1 ) siz[a[i]] = siz[ch[a[i]][0]] + siz[ch[a[i]][1]] + 1;

    LL ans = L == 1 ? solve( a[1] ) : 0;
    rep ( i, 1, n ) if ( b[i] < L || R < b[i] ) {
        ans += siz[i];
        if ( L <= b[ch[i][0]] && b[ch[i][0]] <= R ) ans += solve( ch[i][0] );
        if ( L <= b[ch[i][1]] && b[ch[i][1]] <= R ) ans += solve( ch[i][1] );
    }
    printf( "%lld\n", ans );
    return 0;
}

posted @   Rainybunny  阅读(117)  评论(1编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示