zoukankan      html  css  js  c++  java
  • Solution -「Gym 102798K」Tree Tweaking

    (mathcal{Description})

      Link.

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

      (nle10^5)(r-l+1le200)

    (mathcal{Solution})

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

    [f(l,r)=left(r-l+sum_{iin[l,r]}a_i ight)+min_{pin[l,r)}{f(l,p)+f(p+1,r)}. ]

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

    (mathcal{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
    ", ans );
        return 0;
    }
    
    
  • 相关阅读:
    sizeof
    {面试题10: 二进制中1的个数}
    {面试题9: 斐波那契数列}
    gcc常用选项
    mount.nfs: access denied by server while mounting localhost:/home/xuwq/minilinux/system
    ubuntu bash提示找不到文件或目录
    linux 编译内核[scripts/kconfig/dochecklxdialog] 错误
    最简单的抓包程序
    linux命令行解析函数介绍
    逻辑地址到线性地址的转换
  • 原文地址:https://www.cnblogs.com/rainybunny/p/15164792.html
Copyright © 2011-2022 走看看