hihocoder 1193 树堆 解题报告

题目大意:给出一棵有根树(根为 0 ),点有点权。可以删除点(非根),并将其子树接到其父亲上。我们称一个树为树堆当前仅当树上每个点都满足其权值大于等于其子树中所有点的点权。现在对于每个点要求其子树删去一些点后形成的树堆的最大大小,其中这个点在其自身的子问题内是不可删除的。

题解

这个每个点的子树都求一遍一眼就可以看出是 线段树合并 或者 dsu on tree ,然后略加思索发现这个维护的过程类似 DP ,可以发现不太适合 dsu on tree ,因此选择 线段树合并 。现在考虑怎么维护。

首先我们将点权离散化,然后每个点开一个线段树用线段树的每个位置维护点权为 k 的值加在当前点的父亲上(这个新加的点不可删除)时可以得到的最大子树大小。那么对于一个点 u,我们将其子树的线段树全部合并起来,具体来说是线段树的每个节点的权值相加(因为各个子树互不干扰)。点 u 的答案直接查询其合并完的线段树中 val[u] 位置的值 +1 即可得到。但是之后向上合并的时候这个点是可以被删除了,这个要怎么维护?

发现当前的线段树维护的是没加入当前点的,也就是说它维护的还是对于每个权值 k 其子树可以得到的最大大小,只需要以合适的方法加入 u 的信息即可。如果上方接入父亲的节点的权值比当前点的大,那么显然可以拿到这个点的答案,但是也可能不选择这个点更优,我们只需要对于线段树的从 val[u] 开始的后缀的这段区间的所有点对于 ans[u]max 即可,但是 maxadd 的标记同时维护起来巨难,无法知晓标记先后顺序。但是不用怕,我们可以把取 max 转换成加法。发现 ans[u] 是之前的线段树查询出的结果 +1 ,即线段树上 val[u] 位置上的值是 ans[u]1 ,那么我们只需要找到最后一个线段树中权值为 ans[u] 的位置 r,把区间 [val[u],r) 进行区间 +1 即可。(因为我们取 max 是从 val[u] 这个位置开始的)

具体一些实现细节看代码吧:

#define ll long long
#define db double
#define filein(a) freopen(#a".in","r",stdin)
#define fileot(a) freopen(#a".out","w",stdout)
#define sky fflush(stdout);
#define gc getchar
#define pc putchar
namespace IO{
	inline bool blank(const char &c){
		return c==' ' or c=='\n' or c=='\t' or c=='\r' or c==EOF;
	}
	inline void gs(char *s){
		char ch=gc();
		while(blank(ch) ) {ch=gc();}
		while(!blank(ch) ) {*s++=ch;ch=gc();}
		*s=0;
	}
	inline void gs(std::string &s){
		char ch=gc();s+='#';
		while(blank(ch) ) {ch=gc();}
		while(!blank(ch) ) {s+=ch;ch=gc();}
	}
	inline void ps(char *s){
		while(*s!=0) pc(*s++);
	}
	inline void ps(const std::string &s){
		for(auto it:s) 
			if(it!='#') pc(it);
	}
	template<class T>
	inline void read(T &s){
		s=0;char ch=gc();bool f=0;
		while(ch<'0'||'9'<ch) {if(ch=='-') f=1;ch=gc();}
		while('0'<=ch&&ch<='9') {s=s*10+(ch^48);ch=gc();}
		if(ch=='.'){
			db p=0.1;ch=gc();
			while('0'<=ch&&ch<='9') {s=s+p*(ch^48);p*=0.1;ch=gc();}
		}
		s=f?-s:s;
	}
	template<class T,class ...A>
	inline void read(T &s,A &...a){
		read(s);read(a...);
	}
};
using IO::read;
using IO::gs;
using IO::ps;
const int N=1e5+3;
int n;
int a[N];
namespace Discrete{
	int id[N];
	inline void work(){
		for(int i=1;i<=n;++i){
			id[i]=i;
		}
		std::sort(id+1,id+1+n,[](int x,int y){
			return a[x]<a[y];
		});
		int la=-1e9,top=0;
		for(int i=1;i<=n;++i){
			if(a[id[i] ]!=la) ++top;
			la=a[id[i] ];
			a[id[i] ]=top;
		}
	}
};
int head[N],nxt[N<<1];
struct Edge{
	int u,v;
}to[N<<1];
int Etot=-1;
inline void link(int u,int v){
	nxt[++Etot]=head[u];
	head[u]=Etot;
	to[Etot]={u,v};
}
inline void link1(int u,int v){
	link(u,v);link(v,u);
}
struct SegTree{
	int rt[N];
	int tot;
	#define lc(x) t[x].lc
	#define rc(x) t[x].rc
	struct node{
		int lc,rc;
		int mx;
		int add;
	}t[N*40];
	inline void pushup(int x){
		t[x].mx=std::max(t[lc(x)].mx,t[rc(x)].mx);
	}
	inline bool leaf(int x){
		return !lc(x) and !rc(x);
	}
	inline void change(int x,int add){
		if(!x) return;
		t[x].add+=add;t[x].mx+=add;
	}
	inline void pushdown(int x){
		if(t[x].add){
			if(!lc(x) ) lc(x)=++tot;
			if(!rc(x) ) rc(x)=++tot;
			change(lc(x),t[x].add);
			change(rc(x),t[x].add);
			t[x].add=0;
		}
	}
	void modify(int &x,int l,int r,int ql,int qr,int k){
		if(!x){
			x=++tot;
			t[x].mx=t[x].add=0;
			t[x].lc=t[x].rc=0;
		}
		if(ql<=l and r<=qr){
			change(x,k);
			return;
		}
		pushdown(x);
		int mid=(l+r)>>1;
		if(ql<=mid) modify(lc(x),l,mid,ql,qr,k);
		if(mid+1<=qr) modify(rc(x),mid+1,r,ql,qr,k);
		pushup(x);
	}
	int merge(int x,int y){
		if(!x or !y) return x|y;
		if(leaf(x) ) std::swap(x,y);
		if(leaf(y) ){
			change(x,t[y].mx);
			return x;
		}
		pushdown(x);pushdown(y);
		lc(x)=merge(lc(x),lc(y) );
		rc(x)=merge(rc(x),rc(y) );
		pushup(x);
		return x;
	}
	int find(int x,int l,int r,int p){
		if(leaf(x) ){
			return t[x].mx;
		}
		pushdown(x);
		int mid=(l+r)>>1;
		if(p<=mid) return find(lc(x),l,mid,p);
		else return find(rc(x),mid+1,r,p);
	}
	int pos(int x,int l,int r,int p){
		if(t[x].mx<p) return r+1;
		if(leaf(x) ) return l;
		pushdown(x);
		int mid=(l+r)>>1;
		if(p<=t[lc(x)].mx) return pos(lc(x),l,mid,p);
		else return pos(rc(x),mid+1,r,p);
	}
}t;
int ans[N];
void dfs(int u,int f){
	for(int i=head[u];~i;i=nxt[i]){
		int v=to[i].v;
		if(v==f) continue;
		dfs(v,u);
		t.rt[u]=t.merge(t.rt[u],t.rt[v]);
	}
	ans[u]=t.find(t.rt[u],1,n,a[u])+1;
	int r=t.pos(t.rt[u],1,n,ans[u])-1;
	t.modify(t.rt[u],1,n,a[u],r,1);
}
int main(){
	filein(treap);fileot(treap);
	read(n);
	memset(head,-1,sizeof(head) );
	for(int i=1;i<=n;++i){
		read(a[i]);
	}
	for(int i=1;i<n;++i){
		int u,v;
		read(u,v);
		++u;++v;
		link1(u,v);
	}
	Discrete::work();
	dfs(1,1);
	for(int i=1;i<=n;++i){
		printf("%d ",ans[i]);
	}
	return 0;
}

主要是看到网上的题解讲两句就结束了,很多东西只字未提。只觉得这样的题解没有意义,纯粹是浪费时间贴个代码大可不必。于是我就发了一篇详细的。

posted @   cbdsopa  阅读(41)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示