• [Gym 102798K] Tree Tweaking


    一、题目

    点此看题

    给出一个 (n) 个点的排列 (p),现在要把这 (n) 个点按顺序建立二叉查找树,问把 ([L,R]) 这段区间重排之后所得搜索树的最小深度和是多少。

    (nleq 10^5,R-L< 200)

    二、解法

    首先要知道如何建树,虽然我们不知道二叉搜索树怎么建但是知道 ( t Treap) 怎么建,考虑二叉搜索树其实就是第 (i) 个点的 (heap[i]=i)( t Treap),我们考虑依据这个性质构建。

    先把所有点按照 ((p_i,i)) 放在坐标系上,然后发现要满足堆性质就要选纵坐标最小的点当根,然后向两边连左右儿子,横坐标自然满足二叉查找树的性质,发现这就是笛卡尔树,直接拿栈构建即可。

    下面是 (p={3,1,2,5,4}) 利用上述方法建出的树:

    然后考虑重排 ([L,R]) 其实就是任意交换 (yin[L,R]) 这些点的 (x) 坐标,我们把这些点染色,考虑每个极大染色连通块内都只改变内部的结构,而且不会相互影响,其他连通块的结构都不变。原因很简单,因为重排 ([L,R]) 并不改变其他点和染色点的偏序关系

    那么考虑规划重排过程,对每个极大连通块分别考虑,因为子树大小和等于深度和,那么我们最小化染色点的子树大小和即可。设连通块大小为 (m),我们可以把这个连通块所接的 (m+1) 个子树全部取出来,把它们的 (siz) 按中序遍历排列成 (a),每次拿一个根然后把区间划分成两部分,这是显然的区间 (dp),设 (f(l,r)) 为规划区间 ([l,r]) 所得的最小子树和:

    [f(l,r)=(r-l+sum_{iin[l,r]}a_i)+min(f(l,k),f(k+1,r)) ]

    时间复杂度 (O(n+(R-L)^3))

    三、总结

    二叉搜索树可以转成笛卡尔树建立,这启示我们简单结构可以从复杂结构反推

    树重排规划问题联想区间 (dp),因为它的枚举断点过程其实就是树枚举根的过程。

    #include <cstdio>
    #include <iostream>
    using namespace std;
    const int M = 100005;
    #define ll long long
    int read()
    {
        int num=0,flag=1;char c;
        while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
        while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
        return num*flag;
    }
    int n,m,L,R,a[M],b[M],st[M],siz[M],ch[M][2];
    ll f[205][205],s[M],ans;
    void dfs(int u)
    {
    	if(!u || b[u]>R) {s[++m]=siz[u];return ;}
    	dfs(ch[u][0]);dfs(ch[u][1]);
    }
    ll solve(int x)
    {
    	m=0;dfs(x);
    	for(int i=1;i<=m;i++) s[i]+=s[i-1];
    	for(int i=m;i>=1;i--)
    		for(int j=i+1;j<=m;j++)
    		{
    			f[i][j]=1ll<<60;
    			for(int k=i;k<j;k++)
    				f[i][j]=min(f[i][j],f[i][k]+f[k+1][j]);
    			f[i][j]+=j-i+s[j]-s[i-1];
    		}
    	return f[1][m];
    }
    signed main()
    {
    	n=read();
    	for(int i=1;i<=n;i++)
    		a[i]=read(),b[a[i]]=i;
    	L=read();R=read();
    	for(int i=1;i<=n;i++)
    	{
    		while(m && b[st[m]]>b[i]) ch[i][0]=st[m--];
    		if(m) ch[st[m]][1]=i;
    		st[++m]=i;
    	}
    	for(int i=n;i>=1;i--)
    		siz[a[i]]=siz[ch[a[i]][0]]+siz[ch[a[i]][1]]+1;
    	ans=L==1?solve(a[1]):0;
    	for(int i=1;i<=n;i++) 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);
    }
    
  • 相关阅读:
    软件工程逃课小组——冲刺集合
    软件工程逃课小组——冲刺日志(第一天)
    2020软件工程作业05
    软工实践第四次作业
    第三次
    第二次作业
    2020软件工程作业01
    Ubantu18安装SU(Seismic Unix)脚本
    Win10下通过anaconda搭建新环境并安装tensorflow-gpu
    Win10安装虚拟机(Ubantu18.04)并安装seismic unix(SU)
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15201894.html
Copyright © 2020-2023  润新知