https://www.9duan.com/public-contests/107/room?practice=1

模拟赛T2,这期神了

严肃发现我是图论虫且……

https://www.9duan.com/public-contests/107/room?practice=1

题面

题目描述

一片山区有 \(n\) 个观测点,编号为 \(1,2,\dots,n\),它们由 \(n-1\) 条双向道路连接,任意两个观测点之间都存在唯一的一条简单路径。第 \(i\) 个观测点的高度为 \(a_i\)。

从观测点 \(s\) 前往观测点 \(t\) 时,观察员沿这条唯一路径行走,并按经过顺序记录高度。起点一定被记入笔记;之后每到达一个观测点,只有它的高度严格大于此前经过的所有观测点的高度时,才把它记入笔记。

用 \(R(s,t)\) 表示这次行走记入笔记的观测点个数。如果 \(s=t\),则 \(R(s,t)=1\)。

请对每个起点 \(s\),计算它前往所有终点时的记录总数

\[F_s = \sum_{t=1}^{n} R(s,t). \]

输入格式

第一行包含一个整数 \(n\)。

第二行包含 \(n\) 个整数 \(a_1,a_2,\dots,a_n\)。

接下来 \(n-1\) 行,每行包含两个整数 \(u,v\),表示观测点 \(u,v\) 之间有一条道路。

输出格式

输出一行 \(n\) 个整数,依次表示 \(F_1,F_2,\dots,F_n\)。

输入格式

第一行包含一个整数 (n)。

第二行包含 (n) 个整数 (a_1,a_2,\dots,a_n)。

接下来 (n-1) 行,每行包含两个整数 (u,v),表示观测点 (u,v) 之间有一条道路。

输出格式

输出一行 \((n)\) 个整数,依次表示\((F_1,F_2,\dots,F_n)\)。

题意简述

一棵树,\(n\) 个点 (\(n\le2×10^5\)),点有点权。分别求从每个点出发所能走的所有路径中每条路径的最长上升子序列的长度之和。

Solution

我最开始还以为是dp……

如果我们枚举被贡献点(起点),难以忽视终点,可以用换根dp做到复杂度\(O(n^2)\),但这不是很有前途。

我们考虑枚举贡献点 \(u\)(路径中的任一点)。对于任意一个起点 \(s\) ,如果 \(u\) 对 \(s\) 有贡献,那么必然有:

树上从 \(s\) 到 \(u\) 的路径上,一定不存在一点 \(v\), 使得 \(v\) 的点权比 \(u\) 小

换言之,在计算点 \(u\) 的贡献时,我们可以把点权大于或等于 \(u\) 的点权的结点全部当成滚木,得到一张新图。此时如果我们再删去点 \(u\), 那么原图变成一些连通块。我们设任一连通块(把滚木也算上) \(i\) 的大小 \(sz\),那么点 \(u\) 对连通块 \(i\) 中任一点的贡献即为 \(n-sz_i\)。这很好。

我们思考怎么做这个过程。直接暴力去做,复杂度是 \(O(n^2)\),原因是每次都要重新建一张 \(|V| = n\) 的图。

因此,我们要避免重建的过程。可以发现,如果我们将点按点权从小到大排序,那么可用起点最初只有滚木,而后逐步扩展变为原图。点只会由滚木变为可用点而不会由可用点变为滚木。这很好。这意味着可以 \(O(n)\) 维护建图过程了。

那么连通块的操作怎么维护?连通块本身我们肯定是用并查集维护,整个连通块的修改操作也就变成带权并查集的修改操作。对于每个点,让它一直跳并查集上的父亲并计算点权之和即为答案。

并查集带权,可以每次开一个新点并最后建成一棵树,这个复杂度是反阿克曼的

也可以不开新点,只使用按秩合,这个复杂度是log的

这里使用第二种方法实现

实现的时候有些小细节要注意。

Code

#include <bits/stdc++.h>
#define il inline
#define rg register
#define int ll
using ll = long long;
using namespace std;
namespace space{
    const char endl = '\n';
    const int maxn = 2e5+50;
    vector<int> g[maxn];
    bitset<maxn> gm;//判断这个点是(0)不是(1)滚木,或者说,该点是否可用
    int n;
    struct point{
        int val,id;
        operator<(const point & tmp) const{
            return val<tmp.val;
        }
    } a[maxn];
    int sz2[maxn],fat[maxn];
    void dfs(int u,int fa){
        fat[u] = fa;
        sz2[u] = 1;
        for(int v:g[u]){
            if(v==fa) continue;
            dfs(v,u);
            sz2[u] += sz2[v];
        }
    }
    //DSU
    int fa[maxn],val[maxn],sz[maxn];
    void init(){
        for(int i = 1;i<=n;i++){
            fa[i] = i;
            sz[i] = 1;
        }
    }
    il int find(int u){
        if(fa[u] == u) return u;
        return find(fa[u]);
    }
    il void merge(int u,int v){
        int fau = find(u);
        int fav = find(v);
        if(fau == fav) return ;
        if(sz[fau]<sz[fav]) swap(fau,fav);
        sz[fau] += sz[fav];
        fa[fav] = fau;
        val[fav] -= val[fau];//避免继承先前就有的标记
    }
    signed main(){
        ios::sync_with_stdio(0);
        cin.tie(0);cout.tie(0);
	    cin >> n;
	    init();
	    for(int i = 1;i<=n;i++){
            cin >> a[i].val;
            a[i].id = i;
	    }
	    for(int i = 1;i<n;i++){
            int u,v;
            cin >> u >> v;
            g[u].push_back(v);
            g[v].push_back(u);
	    }
        sort(a+1,a+1+n);
        int cur = 1;
        dfs(1,0);
        for(int i = 1;i<=n;i++){
            int u = a[i].id;
            while(a[cur].val<a[i].val){
                gm[a[cur].id] = 1;
                for(int v : g[a[cur].id]){
                    if(gm[v]) merge(a[cur].id,v);
                }
                cur++;
            }
            int S = 0;
            for(int v : g[u]){
                if(!gm[v]) continue;
                int x = find(v);
            }
            for(int v : g[u]){
                if(!gm[v]) continue;
                int x = find(v);
                if(fat[u] == v) val[x] += sz2[u];
                else val[x] += n-sz2[v];
            }
            for(int v:g[u]){
                if(!gm[v]) continue;
                int x = find(v);
            }
        }
        for(int i = 1;i<=n;i++){
            int ans = 0;
            int u = i;
            while(fa[u]!=u){
                ans += val[u];
                u = fa[u];
            }
            ans += val[u];
            cout << ans+n << ' ';
        }
	    return 0;
    }
}
signed main(){
    #ifdef ONLINE_JUDGE
    #else
        freopen("in.in","r",stdin);
        freopen("out.out","w",stdout);
    #endif
        space::main();
        return 0;
}
posted @ 2026-10-04 20:25  _2e21  阅读(3)  评论(0)    收藏  举报