Luogu P8990

题面

注意到直接根据题目的条件判断树是否美丽并不容易。考虑未被点亮的点,可以发现一棵树是美丽的当且仅当未被点亮的点形成一个连通块

有一个结论是,对于一个森林,点数减边数等于连通块的个数。()

因此树是美丽的当且仅当 “未被点亮的节点的个数”减去“两端都未被点亮的边的个数” =1。令 P 表示这个值,当且仅当 P=1 时树是美丽的。

同时根据 结论,树上被点亮的连通块的个数就是 “被点亮的节点的个数”减去“两端都被点亮的边的个数”。设这个值为 Q

如果我们按顺序执行点亮操作,那么每个时刻的 PQ 会分别形成一个序列。更准确地说,令 pi 表示点亮完第 ai 个点后的 P 值,qi 表示点亮完第 ai 个点后的 Q 值,我们就得到了 pq 两个序列,答案即为 i=1n1qi×[pi=1]。([X]X 为真时 =1,否则 =0

接下来考虑 link-cut 操作,先考虑断掉一条边 (u,v) 时对 pq 的影响:由于一个节点是否点亮只决定于 i,所以边的情况不会影响点的情况。记 ti 表示点 i 被点亮的时刻,则 (u,v) 脱离“两端都未被点亮”状态的时刻就是 min(tu,tv),在这之前它都对 p 有贡献,因此删去它后会使 p[1,min(tu,tv)1] 区间每个数 +1。它变成“两端都被点亮”的状态的时刻是 max(tu,tv),在这之后它都对 q 有贡献,因此删去它后会使 q[max(tu,tv),n1] 区间每个数 +1

同理,如果连上一条边 (x,y),则它会使 p[1,min(tx,ty)1] 区间 1q[max(tx,ty),n1] 区间 1

于是转化成了一个线段树问题。具体地,对于线段树每个节点 [l,r],维护 mn=mini=lrpicnt=i=lr[pi=mn]sum=i=lrqi×[pi=mn] 以及加法标记即可。

时间复杂度 O((n+m)logn)

#include<bits/stdc++.h>
#define endl '\n'
#define rep(i, s, e) for(int i = s, i##E = e; i <= i##E; ++i)
#define per(i, s, e) for(int i = s, i##E = e; i >= i##E; --i)
#define F first
#define S second
#define int ll
#define gmin(x, y) (x = min(x, y))
#define gmax(x, y) (x = max(x, y))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double f128;
typedef pair<int, int> pii;
constexpr int N = 5e5 + 5;
int n, m, a[N], t[N];
struct edge {
    int u, v;
} e[N];
struct node {
    int mn, sum, cnt, t1, t2;
} nd[N * 4];
#define ls (p << 1)
#define rs (p << 1 | 1)
void upd(int p, int v, int w) {
    nd[p].mn += v;
    nd[p].t1 += v;
    nd[p].sum += nd[p].cnt * w;
    nd[p].t2 += w;
}
void pushdown(int p) {
    if(nd[p].t1 || nd[p].t2) {
        upd(ls, nd[p].t1, nd[p].t2);
        upd(rs, nd[p].t1, nd[p].t2);
        nd[p].t1 = nd[p].t2 = 0;
    }
}
void pushup(int p) {
    if(nd[ls].mn < nd[rs].mn) nd[p].mn = nd[ls].mn, nd[p].sum = nd[ls].sum, nd[p].cnt = nd[ls].cnt;
    else if(nd[ls].mn > nd[rs].mn) nd[p].mn = nd[rs].mn, nd[p].sum = nd[rs].sum, nd[p].cnt = nd[rs].cnt;
    else nd[p].mn = nd[ls].mn, nd[p].sum = nd[ls].sum + nd[rs].sum, nd[p].cnt = nd[ls].cnt + nd[rs].cnt;
}
void build(int p, int l, int r) {
    if(l == r) {
        nd[p].mn = n - l;
        nd[p].sum = l;
        nd[p].cnt = 1;
        return;
    }
    int mid = (l + r) / 2;
    build(ls, l, mid), build(rs, mid + 1, r);
    pushup(p);
}
void add(int p, int l, int r, int ql, int qr, int v, int w) {
    if(l > qr || r < ql) return;
    if(ql <= l && qr >= r) return upd(p, v, w);
    pushdown(p);
    int mid = (l + r) / 2;
    add(ls, l, mid, ql, qr, v, w);
    add(rs, mid + 1, r, ql, qr, v, w);
    pushup(p);
}
signed main() {
#ifdef ONLINE_JUDGE
    ios::sync_with_stdio(0);
    cin.tie(0), cout.tie(0);
#endif
    cin >> n >> m;
    build(1, 1, n - 1);
    rep(i, 1, n - 1) cin >> e[i].u >> e[i].v;
    rep(i, 1, n - 1) cin >> a[i], t[a[i]] = i;
    t[1] = n;
    rep(i, 1, n - 1) {
        int u = e[i].u, v = e[i].v;
        add(1, 1, n - 1, 1, min(t[u], t[v]) - 1, -1, 0);
        add(1, 1, n - 1, max(t[u], t[v]), n - 1, 0, -1);
    }
    cout << (nd[1].mn == 1 ? nd[1].sum : 0) << endl;
    while(m--) {
        int u, v, x, y;
        cin >> u >> v >> x >> y;
        add(1, 1, n - 1, 1, min(t[u], t[v]) - 1, 1, 0);
        add(1, 1, n - 1, max(t[u], t[v]), n - 1, 0, 1);
        add(1, 1, n - 1, 1, min(t[x], t[y]) - 1, -1, 0);
        add(1, 1, n - 1, max(t[x], t[y]), n - 1, 0, -1);
        cout << (nd[1].mn == 1 ? nd[1].sum : 0) << endl;
    }
    return 0;
}
posted @   untitled0  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· AI与.NET技术实操系列(六):基于图像分类模型对图像进行分类
点击右上角即可分享
微信分享提示