CF1578J题解

容易发现分配给一个子树的钱只要够了就会移除

具体来讲,如果一个结点被分配到了 \(x\) 块钱,那么有两种情况:

  1. 子树全部都拿到了该拿的钱,自己拿到了一部分或者全部拿到了
  2. 对于每个儿子,其子树拿到的钱均不超过某个值

对于情况 1 容易构造使其不发生,对于情况 2 可以每次二分。

\(O(n^2\log V)\) 的算法就是每次跳父亲,然后计算当前节点应该拿多少钱。

考虑对于一个节点 \(u\) 维护一颗平衡树,平衡树上存着子树内所有节点在当前节点的答案。

对于 \(u\) 的某个儿子应该拿到 \(x\) 块钱,那么跳了父亲之后他应该拿到 \(x+\sum\min(sum_v,x)\) 的钱。

容易发现对于 \(u\) 平衡树中每个元素更新后的相对大小是不变的,所以可以更新之后与兄弟的平衡树进行启发式合并。但是更新的复杂度过高。

假设对于 \(x\)\(u\) 的兄弟中满足 \(x>sum\) 比其小的 \(sum\) 之和为 \(S\),满足 \(x<=sum\) 的节点数量为 \(k\)(包括 \(u\)),那么更新后其值为 \(x\times k+S\)

可以发现先进行启发式合并后再更新元素的值与先更新再启发式合并是相同的,这样一来复杂度就降低到了 \(O(n\log^2n)\)

#include<algorithm>
#include<cstdlib>
#include<cstdio>
#include<cctype>
#include<ctime>
typedef long long ll;
const int M=3e5+5;
int n,dfe,h[M],f[M],rt[M],siz[M];ll S[M];
inline int read(){
	int n(0);char s;while(!isdigit(s=getchar()));while(n=n*10+(s^48),isdigit(s=getchar()));return n;
}
inline void write(ll n){
	static char s[20];int top(0);while(s[++top]=n%10^48,n/=10);while(putchar(s[top]),--top);
}
struct line{
	ll k,b;
	line(const ll&k=1,const ll&b=0):k(k),b(b){}
	inline line operator()(const line&it){
		return line(k*it.k,k*it.b+b);
	}
	inline ll operator()(const ll&x){
		return k*x+b;
	}
};
namespace fhq{
	int rnd[M],chi[M][2];ll w[M];line tag[M];
	inline void nnd(const int&u,const ll&V){
		rnd[u]=rand()*rand();chi[u][0]=chi[u][1]=0;tag[u]=line();w[u]=V;
	} 
	inline void pushdown(const int&u){
		const int&ls=chi[u][0],&rs=chi[u][1];
		if(ls)w[ls]=tag[u](w[ls]),tag[ls]=tag[u](tag[ls]);
		if(rs)w[rs]=tag[u](w[rs]),tag[rs]=tag[u](tag[rs]);
		tag[u]=line();
	}
	inline void split(const int u,const ll&V,int&x,int&y){
		if(!u)return void(x=y=0);pushdown(u);
		if(w[u]<=V)split(chi[x=u][1],V,chi[u][1],y);
		else split(chi[y=u][0],V,x,chi[u][0]);
	}
	inline int merge(const int q,const int p){
		if(!q||!p)return q|p;pushdown(q);pushdown(p);
		if(rnd[q]<rnd[p])return chi[q][1]=merge(chi[q][1],p),q;
		else return chi[p][0]=merge(q,chi[p][0]),p;
	}
	inline void ins(const int u,int&rt){
		int x(0),y(0);split(rt,w[u],x,y);rt=merge(merge(x,u),y);
	}
	inline void Mrg(int u,int&rt){
		if(!u)return;pushdown(u);Mrg(chi[u][0],rt);Mrg(chi[u][1],rt);chi[u][0]=chi[u][1]=0;ins(u,rt);
	}
}
using namespace fhq;
struct Edge{
	int v,nx;
}e[M];
inline void Add(const int&u,const int&v){
	e[++dfe]=(Edge){v,h[u]};h[u]=dfe;
}
inline void DFS(const int&u){
	static int m,s[M];ll sum(0);int r(0);
	for(int E=h[u];E;E=e[E].nx){
		const int&v=e[E].v;DFS(v);S[u]+=S[v];
		int x=rt[u],y=rt[v];if(siz[u]>siz[v])x^=y^=x^=y;Mrg(x,rt[u]=y);siz[u]+=siz[v];
	}
	for(int E=h[u];E;E=e[E].nx)s[++m]=e[E].v;std::sort(s+1,s+m+1,[](int x,int y){return S[x]<S[y];});
	for(int i=1;i<=m;sum+=S[s[i++]]){
		int x(0),y(0);line f(m-i+1,sum);split(rt[u],S[s[i]],x,y);rt[u]=y;
		w[x]=f(w[x]);tag[x]=f(tag[x]);r=merge(r,x);
	}
	rt[u]=r;m=0;if(u)nnd(u,S[u]),rt[u]=merge(rt[u],u),++siz[u];
}
inline void dfs(const int&u){
	if(!u)return;pushdown(u);dfs(chi[u][0]);dfs(chi[u][1]);
}
signed main(){
	srand(time(NULL));n=read();for(int i=1;i<=n;++i)Add(f[i]=read(),i),S[i]=read();DFS(0);dfs(rt[0]);
	for(int u=1;u<=n;++u)write(w[u]),putchar('\n');
}
posted @ 2023-11-15 10:59  Prean  阅读(11)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};