Luogu8990 做題記錄

Lgx_Q發表於2024-09-06

link

比較喜歡的題目。

考慮合法的條件,從點亮的燈的角度難以維護。反過來看,從未點亮的燈角度考慮,條件相當於這些燈形成了一個包含 \(1\) 號燈的連通塊。

如何判定這些燈形成一個連通塊?點減邊!設 \(c_i\) 為操作前 \(i\) 次後,未點亮的燈的 \(|V| - |E|\) 的值,那麼 \(c_i = 1\) 即合法。

對於固定的 \(i\)\(|V|\) 是固定的,等於 \(n - i\),可以先初始化 \(c_i = n - i\)。對於 \(|E|\),我們考慮每一條邊的貢獻。設 \(b_u\) 表示 \(u\) 號燈被點亮的時間,那麼對於邊 \((u,v)\),在時刻 \(1\sim \min(b_u, b_v) - 1\) 時有貢獻,令 \(c_{1\sim \min(b_u, b_v) - 1}\) 減一。

對於 \(c_i = 1\),我們考慮如何計算答案。仍然考慮每條邊的貢獻,一條邊有貢獻的條件是兩端的燈一個點亮,一個未點亮。設 \(w_i\)\(i\) 時刻這樣的邊的數量,對於邊 \((u,v)\),令 \(w_{\min(b_u, b_v) \sim \max(b_u, b_v) - 1}\) 加一。我們相當於求 \(\sum_{i = 1} ^ {n - 1} [c_i = 1] w_i\)

多次詢問時,由於我們並不關心樹的形態,而只關心每條邊的貢獻,所以可以輕鬆使用線段樹維護。具體的,維護 \(\min c,\ \sum_i [c_i = \min c], \ \sum_i [c_i = \min c] w_i\) 三個資訊。

點選檢視程式碼
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned ll
#define pir pair <ll, ll>
#define mkp make_pair
#define fi first
#define se second
#define pb push_back
using namespace std;
const ll maxn = 5e5 + 10, inf = 1e18;
ll n, m, x[maxn], y[maxn], a[maxn], b[maxn];
struct SGT {
	ll mn[maxn << 2], cnt[maxn << 2], sum[maxn << 2];
	ll tagmn[maxn << 2], tagw[maxn << 2];
	void addtag(ll p, ll v1, ll v2) {
		mn[p] += v1, tagmn[p] += v1;
		sum[p] += v2 * cnt[p], tagw[p] += v2;
	}
	void pushdown(ll p) {
		addtag(p << 1, tagmn[p], tagw[p]);
		addtag(p << 1|1, tagmn[p], tagw[p]);
		tagmn[p] = tagw[p] = 0;
	}
	void modify(ll p, ll l, ll r, ll ql, ll qr, ll v1, ll v2) {
		if(ql <= l && r <= qr) { addtag(p, v1, v2); return; }
		pushdown(p); ll mid = l + r >> 1;
		if(ql <= mid) modify(p << 1, l, mid, ql, qr, v1, v2);
		if(mid < qr) modify(p << 1|1, mid + 1, r, ql, qr, v1, v2);
		mn[p] = min(mn[p << 1], mn[p << 1|1]), cnt[p] = sum[p] = 0;
		if(mn[p << 1] == mn[p])
			cnt[p] += cnt[p << 1], sum[p] += sum[p << 1];
		if(mn[p << 1|1] == mn[p])
			cnt[p] += cnt[p << 1|1], sum[p] += sum[p << 1|1];
	}
	void build(ll p, ll l, ll r) {
		mn[p] = n - r, cnt[p] = 1;
		if(l == r) return;
		ll mid = l + r >> 1;
		build(p << 1, l, mid), build(p << 1|1, mid + 1, r);
	}
} tr;
void add(ll x, ll y, ll v) {
	if(min(b[x], b[y]) > 1)
		tr.modify(1, 1, n - 1, 1, min(b[x], b[y]) - 1, -v, 0);
	tr.modify(1, 1, n - 1, min(b[x], b[y]), max(b[x], b[y]) - 1, 0, v);
}
int main() {
	scanf("%lld%lld", &n, &m);
	for(ll i = 1; i < n; i++)
		scanf("%lld%lld", x + i, y + i);
	for(ll i = 1; i < n; i++) {
		scanf("%lld", a + i);
		b[a[i]] = i;
	} b[a[n] = 1] = n; tr.build(1, 1, n - 1);
	for(ll i = 1; i < n; i++)
		add(x[i], y[i], 1);
	printf("%lld\n", tr.sum[1]);
	while(m--) {
		ll u, v, p, q; scanf("%lld%lld%lld%lld", &u, &v, &p, &q);
		add(u, v, -1), add(p, q, 1);
		printf("%lld\n", tr.sum[1]);
	}
	return 0;
}