题解 AVL 树

传送门

  • 注意一个事情:当0下标是一个合法点的时候不能让它当哨兵,随意修改值!

题意大概是给一棵AVL树,要求删去 \(n-k\) 个点使其仍是AVL树且要求字典序最小
考场上觉得不可做就弃了
参考了dalao博客dalao博客dalao博客
有趣的是,这三篇博客分别是 \(O(n), O(nlogn)\)\(O(nlog^2n)\)

首先一定有解
然后考虑怎么让字典序最小
肯定是尽可能保留标号最小的点
于是可以贪心
那就要check能否保留一个点
发现若确定要保留一个点,那它的所有祖先节点(若它在这个祖先节点的左子树中)的右子树深度就确定了(一个下界)
于是考虑维护一个 \(g_i\) 表示深度为 \(i\) 的AVL树至少需要几个节点
转移较显然

\[g_i=g_{i-1}+g_{i-2}+1 \]

check的话就爆跳父亲,若当前点在左子树就看下右边至少要分配多少点
于是按中序遍历(因为祖先节点不能选其子节点一定不能选)贪心就行了……吗?
于是你会对着一个 Wrong Answer 26 一筹莫展
第二个dalao博客提供了hack
于是需要维护一个东西记录当前节点所在子树的深度下限
值得一提的是在更新的时候对于一个祖先节点,直接将标记打在这个祖先节点的右子树上会省好多事
然后我因为没特判祖先节点没有右儿子的情况零零散散调了三天
标记下传的话就传给有能力达到这个深度的子树
复杂度 \(O(nlogn)\),但其实改成先序遍历可以优化到 \(O(n)\)

Code:


#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 500010
#define ll long long
//#define int long long

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n, k;
int f[N], g[N], ls[N], rs[N], dep[N], mdep[N], tag[N], rot;
bool vis[N];

void dfs1(int u) {
	mdep[u]=dep[u];
	if (ls[u]) dep[ls[u]]=dep[u]+1, dfs1(ls[u]), mdep[u]=max(mdep[u], mdep[ls[u]]);
	if (rs[u]) dep[rs[u]]=dep[u]+1, dfs1(rs[u]), mdep[u]=max(mdep[u], mdep[rs[u]]);
}

void dfs2(int u) {
	int now=f[u], lst=u, sum=1, d=0;
	while (~now) {
		if (lst==ls[now]) {
			sum+=g[max(d, tag[now])];
			if (sum>k) break;
		}
		++d; lst=now; now=f[now];
	}
	if (sum<=k) {
		--k; vis[u]=1;
		now=f[u], lst=u, d=0;
		while (~now) {
			tag[now]=max(tag[now], d);
			++d; lst=now; now=f[now];
		}
		if (ls[u]&&mdep[ls[u]]>=dep[u]+tag[u]) tag[ls[u]]=max(tag[ls[u]], tag[u]-1), tag[rs[u]]=max(tag[rs[u]], tag[u]-2);
		else tag[ls[u]]=max(tag[ls[u]], tag[u]-2), tag[rs[u]]=max(tag[rs[u]], tag[u]-1);
		if (ls[u]) dfs2(ls[u]);
		if (rs[u]) dfs2(rs[u]);
	}
}

signed main()
{
	freopen("avl.in", "r", stdin);
	freopen("avl.out", "w", stdout);

	n=read(); k=read();
	g[1]=1;
	for (int i=1; i<=n; ++i) g[i]=g[i-1]+g[i-2]+1;
	for (int i=1; i<=n; ++i) {
		f[i]=read();
		if (f[i]!=-1) {
			if (f[i]>i) ls[f[i]]=i;
			else rs[f[i]]=i;
		}
		else rot=i;
	}
	dep[rot]=1; dfs1(rot);
	dfs2(rot);
	for (int i=1; i<=n; ++i) printf("%d", vis[i]);
	printf("\n");

	return 0;
}
posted @ 2021-11-01 16:59  Administrator-09  阅读(3)  评论(0编辑  收藏  举报