[做題記錄-數據結構]Luogu P7889 「MCOI-06」Eert Tuc Knil 題解


題意

給定一顆 \(n\) 個節點有根樹,第 \(i\) 節點權值為 \(a_i\)

在這個樹上支持一種詢問:

給定節點 \(u\) 和參數 \(x\)假如 所有節點點權加 \(x\)在這種情況下,求: 對於所有完全在 \(u\) 子樹內並包含 \(u\) 的連通點集,權值之和最大可能為多少?

\(n \leq 10^6\)

題解

考慮一個\(\text{dp}\)\(f_x = val_x +\sum \max\{f_y, 0\}\)

可以把查詢離線, 然后只剩下加法。然后考慮如何維護加法。

先對原樹進行一次\(dp\), 然后可以轉移的邊視為存在, 否則不存在。每次找到一條不存在的邊使得其存在即可維護答案。

一條邊不存在當且僅當其連接的連通塊權值小於\(0\)。那么把連通塊按照\(\frac{-val}{sz}\)排序, 每次取出最小的, 判斷是否可以連上, 執行合並即可。

然后現在的問題是如何快速進行合並和維護答案。

維護答案的部分所需要的是維護一個點所連接的子樹內的聯通塊的和以及大小。可以維護一個聯通塊的最高點, 這樣的話就是執行一次鏈加。

那么現在就是要做鏈加和單點查, 可以樹剖\(\log^2\)

換成子樹查可以做到一個\(\log\)

#include <bits/stdc++.h>

using namespace std;

using ll = long long;
using ull = unsigned long long;

template<typename T>
inline void read(T &x) {
	x = 0; char a = getchar(); bool f = 0;
	for(; ! isdigit(a); a = getchar()) if(a == '-') f = 1;
	for(; isdigit(a); a = getchar()) x = x * 10 + a - '0';
	if(f) x = -x;	
} 

#define lep(i, l, r) for(int i = (l); i <= (r); i ++) 
#define rep(i, l, r) for(int i = (r); i >= (l); i --) 

const int N = 1e6 + 10;
const ll Lim = 1e13;

int n, m;

struct BIT {
	ll c[N];
	#define lowbit(x) (x & -x)
	void upd(int x, ll v) {
		for(; x <= n; x += lowbit(x)) c[x] += v;
	}
	ll ask(int x) {
		ll res = 0;
		for(; x; x -= lowbit(x)) res += c[x];
		return res;
	}
	ll qry(int l, int r) {
		return ask(r) - ask(l - 1);
	}
} c1, c2;

int Fa[N];
inline int find(int x) { return x == Fa[x] ? x : Fa[x] = find(Fa[x]); }

int fa[N];
vector<int> e[N];
struct Qry {
	int id, x;
	ll v;
} q[N];

ll val[N];
int dfn[N], sz[N];

void dfs(int x) {
	dfn[x] = ++ dfn[0];
	sz[x] = 1;
	for(int y : e[x]) dfs(y), sz[x] += sz[y];
}

void modify(int x, int y, ll v, int tv) {
	c1.upd(dfn[x], v);
	c2.upd(dfn[x], tv);
	y = fa[y];
	if(y) {
		c1.upd(dfn[y], -v);
		c2.upd(dfn[y], -tv);
	}
}

struct Node {
	ll v; int x;
	Node() {}
	Node(ll _v, int _x) : v(_v), x(_x) {}
	inline bool operator <(const Node &t) const {
		return v != t.v ? v > t.v : x < t.x;	
	}
	inline bool operator ==(const Node &t) const {
		return v == t.v && x == t.x;
	}
} ;

struct Queue {
	priority_queue<Node> q1, q2;
	void push(Node v) {
		q1.push(v);
	}
	void erase(Node v) {
		q2.push(v);
	}
	Node top() {
		while(q1.size() && q2.size() && q1.top() == q2.top()) q1.pop(), q2.pop();
		return q1.top();
	}
	inline int size() {
		return q1.size() - q2.size();
	}
} Q;

ll ans[N];

void link(int x) {
	ll sum = c1.qry(dfn[x], dfn[x] + sz[x] - 1);
	int ssz = c2.qry(dfn[x], dfn[x] + sz[x] - 1);
	int t = find(fa[x]);
	modify(fa[x], t, sum, ssz);
	Fa[x] = t;
	if(t != 1) {
		Q.push( Node( ceil (1.0 * (- c1.qry(dfn[t], dfn[t] + sz[t] - 1)) / c2.qry(dfn[t], dfn[t] + sz[t] - 1)  + 1e-9), t ) );
	}
}

int main() {
	read(n); read(m);
	lep (i, 2, n) read(fa[i]), e[fa[i]].push_back(i);
	lep (i, 1, n) read(val[i]);
	lep (i, 1, m) {
		read(q[i].x); read(q[i].v); q[i].id = i;
	}
	lep (i, 1, n) val[i] -= Lim;
	dfs(1);
	lep (i, 1, n) Fa[i] = i, modify(i, i, val[i], 1); 
	lep (i, 1, m) q[i].v += Lim;
	sort(q + 1, q + 1 + m, [] (Qry a, Qry b) { return a.v < b.v; } );
	lep (i, 2, n) {
		Q.push( Node(- val[i] / 1, i) );
	}
	lep (i, 1, m) {
		while(Q.size() && Q.top().v <= q[i].v) {
			Node tmp = Q.top();
			int fx = fa[tmp.x];
			Q.erase(tmp);
			fx = find(fx);
			if(fx != 1) {
				Q.erase( Node( ceil (1.0 * (- c1.qry(dfn[fx], dfn[fx] + sz[fx] - 1)) / c2.qry(dfn[fx], dfn[fx] + sz[fx] - 1) + 1e-9), fx ) );
			}
			link(tmp.x);
		}
		ans[q[i].id] = c1.qry(dfn[q[i].x], dfn[q[i].x] + sz[q[i].x] - 1) + c2.qry(dfn[q[i].x], dfn[q[i].x] + sz[q[i].x] - 1) * q[i].v;
	}
	lep (i, 1, m) printf("%lld\n", ans[i]);
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM