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;
}