Live2D

【集训队作业2018】三角形

link

Solution

我们考虑到我们加入一个点之后一定立马把所有儿子都收回,所以我们定义 \((a,b)\) 以及它的运算 \((a_1,b_1)+(a_2,b_2)\to (a_1+a_2,\max(a_1+b_2,b_1))\),那么加入一个点 \(u\) 就相当于加入 \((w_u-\sum w_v,w_u)\)

那么就存在一种简单的贪心,即是 \(A+B<B+A\) 就把 A 放 B 的前面。可以证明不会出现环的情况。

但是问题是我们需要先加入儿子再加入父亲,这样我们并不好做了。考虑翻转操作顺序。那么我们现在即是加入 \((\sum w_v-w_u,\sum w_v)\),且在父亲加入之后加入。可以看出这样做是等价的。因为任一时刻在两种加入中都能被表示。其优先顺序也同上。

那么我们就按其顺序来,如果考虑到当前节点,如果其父亲已经加入,那么我们一定立即加入,否则在父亲加入的时刻立马加入。那么我们可以用并查集维护其操作顺序。

注意到对于一个节点,它子树内的操作顺序就是对于根节点的操作顺序的一个子序列,那么我们可以线段树合并直接做。

复杂度 \(\Theta(n\log n)\)

Code

#include <bits/stdc++.h>
using namespace std;
     
#define Int register int
#define int long long
#define MAXN 200005
     
template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < '0' || c > '9'){if (c == '-') f = -f;c = getchar();}while (c >= '0' && c <= '9'){t = (t << 3) + (t << 1) + c - '0';c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar ('-');}if (x > 9) write (x / 10);putchar (x % 10 + '0');}
template <typename T> inline void chkmax (T &a,T b){a = max (a,b);}
template <typename T> inline void chkmin (T &a,T b){a = min (a,b);}

int n,ind,p[MAXN],w[MAXN],b[MAXN],fa[MAXN],w1[MAXN],ans[MAXN];
int findSet (int x){return fa[x] == x ? x : fa[x] = findSet (fa[x]);}

struct node{
	int a,b,id;
	node operator + (const node &p)const{return node{a + p.a,max (a + p.b,b),id};}
	bool operator < (const node &p)const{
		int v1 = (*this + p).b,v2 = (p + *this).b;
		if (v1 != v2) return v1 < v2;
		if (a != p.a) return a < p.a;
		if (b != p.b) return b < p.b;
		return id < p.id;
	}
}s[MAXN];

int rt[MAXN];
vector <int> g[MAXN],h[MAXN];

struct Segment{
#define LOGN 25
#define ls(x) son[x][0]
#define rs(x) son[x][1]
	int cnt;node Sum[MAXN * LOGN];int son[MAXN * LOGN][2];
	void modify (int &x,int l,int r,int pos){
		if (!x) x = ++ cnt;
		if (l == r) return Sum[x] = s[l],void ();
		int mid = l + r >> 1;
		if (pos <= mid) modify (ls(x),l,mid,pos);
		else modify (rs(x),mid + 1,r,pos);
		Sum[x] = Sum[ls(x)] + Sum[rs(x)];
	}
	int merge (int x,int y){
		if (!x || !y) return x + y;
		ls(x) = merge (ls(x),ls(y)),rs(x) = merge (rs(x),rs(y)),Sum[x] = Sum[ls(x)] + Sum[rs(x)];
		return x;
	}
}tree;

bool vis[MAXN];

void dfs (int u){
	b[u] = ++ ind,vis[u] = 1;
	for (Int v : h[u]) dfs (v);
}

void solve (int u){
	tree.modify (rt[u],1,n,b[u]);
	for (Int v : g[u]) solve (v),rt[u] = tree.merge (rt[u],rt[v]);
	ans[u] = w[u] + tree.Sum[rt[u]].b;
}

signed main(){
	int typ;
	read (typ,n);
	for (Int i = 2;i <= n;++ i) read (p[i]),g[p[i]].push_back (i);
	for (Int i = 1;i <= n;++ i) read (w[i]),w1[p[i]] += w[i],fa[i] = i;
	set <node> S;
	for (Int i = 1;i <= n;++ i) s[i] = node{w1[i] - w[i],w1[i],i},S.insert (s[i]);
	while (!S.empty()){
		int u = S.begin() -> id;S.erase (S.begin());
		if (u == 1 || vis[p[u]]) dfs (u);
		else{
			int x = findSet (p[u]);
			S.erase (s[x]),s[x] = s[x] + s[u],S.insert (s[x]),h[x].push_back (u),fa[u] = x;
		}
	}
	for (Int i = 1;i <= n;++ i) s[b[i]] = node{w1[i] - w[i],w1[i],i};
	solve (1);
	for (Int u = 1;u <= n;++ u) write (ans[u]),putchar ('\n');
   	return 0;
}
posted @ 2022-10-24 20:31  Dark_Romance  阅读(14)  评论(0编辑  收藏  举报