P3250 [HNOI2016] 网络 + [NECPC2022] F.Tree Path 树剖+线段树维护堆

两道题思路非常像,因此集中记录一下思路和做法

P3250 [HNOI2016] 网络

题目分析

给定一棵有 n n n个节点的树, 有 m m m次如下操作:

0 a b c 表示在 ( a , b ) (a, b) (a,b)的最短路径上增加一条重要度为 c c c的边.

1 t 表示删除第 t t t次操作所增加的边

2 x 表示节点 x x x出现故障. 此时需要回答, 所有不经过 x x x节点的边中最大的重要度.

首先将边权点权化,也就是树上问题序列化,然后利用用线段树维护待查点的补集。由于线段树节点维护序列最大值,因此将节点开成大根堆,然后每个节点记录不经过该节点的路径中的最大权,以及删除的权值。

对于每个增边操作,树剖LCA求出路径所占的全部区间,然后对其补集进行修改。补集可以先对树剖求出的区间排序,然后按顺序找间隔即可。

Code

#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;

const int N = 1e5 + 10;

struct edge { int u, v, w; } edges[N << 1];

vector<int> g[N];

int pfa[N], dep[N], siz[N], son[N], top[N], dfn[N], rnk[N], ind;
int n, m;

void dfs1(int u, int fa){
    pfa[u] = fa, dep[u] = dep[fa] + 1, siz[u] = 1;
    for(auto &v : g[u]){
        if(v == fa) continue;
        dfs1(v, u);
        siz[u] += siz[v];
        if(siz[v] > siz[son[u]]) son[u] = v;
    }
}

void dfs2(int u, int tp){
    dfn[u] = ++ind, rnk[tp] = 0, top[u] = tp;
    if(!son[u]) return;
    dfs2(son[u], tp);
    for(auto & v: g[u]){
        if(v == pfa[u] || v == son[u]) continue;
        dfs2(v, v);
    }
}


namespace SegmentTree{
    struct node{
        priority_queue<int> val, del;
        int top(){
            while(val.size() && del.size() && val.top() == del.top()) val.pop(), del.pop();
            return val.empty() ? -1 : val.top();
        }
    }tree[N << 2];

    #define ls rt << 1
    #define rs rt << 1 | 1
    #define lson ls, l , mid
    #define rson rs, mid + 1, r

    void update(int rt, int l, int r, int L, int R, int val){
        if(l >= L && r <= R){
            val > 0 ? tree[rt].val.emplace(val) : tree[rt].del.emplace(-val);
            return ;
        }
        int mid = l + r >> 1;
        if(mid >= L) update(lson, L, R, val);
        if(mid < R) update(rson, L, R, val);
    }

    int query(int rt, int l, int r, int pos){
        if(l == r) return tree[rt].top();
        int mid = l + r >> 1, ans = tree[rt].top();
        if(mid >= pos) ans = max(ans, query(lson, pos));
        else ans = max(ans, query(rson, pos));
        return ans;
    }
}

#define pii pair<int,int> 
#define fir first
#define sec second

void update(int u, int v, int w){
    vector<pii> interval;
    while(top[u] != top[v]){
        if(dep[top[u]] < dep[top[v]]) swap(u, v);
        interval.emplace_back(make_pair(dfn[top[u]], dfn[u]));
        u = pfa[top[u]];
    }
    interval.emplace_back(make_pair(min(dfn[u], dfn[v]), max(dfn[u], dfn[v])));
    sort(interval.begin(), interval.end());
    int modl = 1;
    for(auto & [l, r] : interval){
        int modr = l - 1;
        if(modl <= l - 1) SegmentTree::update(1, 1, n, modl, modr, w);
        modl = r + 1;
    }
    if(modl <= n) SegmentTree::update(1, 1, n, modl, n, w);
}


inline void solve(){
     cin >> n >> m;
    for(int i = 1; i <= n - 1; i++){
        int u, v; cin >> u >> v;
        g[u].emplace_back(v);
        g[v].emplace_back(u);
    }
    dfs1(1, 0);
    dfs2(1, 1);
    for(int i = 1; i <= m; i++){
        int op; cin >> op;
        if(op == 0){
            cin >> edges[i].u >> edges[i].v >> edges[i].w;
            update(edges[i].u, edges[i].v, edges[i].w);
        }   
        else if(op == 1){
            int t; cin >> t;
            auto &[u ,v, w] = edges[t];
            update(u, v, -w);
        }
        else{
            int x; cin >> x;
            cout << SegmentTree::query(1, 1, n, dfn[x]) << endl;
        }
    }
}

signed main(){
    ios_base::sync_with_stdio(false), cin.tie(0);
    solve();
    return 0;
}

[NECPC2022] F.Tree Path

给定一棵树,包含 n n n个节点和 k k k条带权路径,要求支持操作:

  • 删除最小的带权路径
  • 对于给定的节点 x x x,输出所有不经过 x x x的带权路径中的最小值

跟上道题思路类似,用线段树维护待查节点的补集,注意这里求得是最小值。

注意必须离散化,否则 m a p map map上多个 l o g log log炸常数过不去。

#include <bits/stdc++.h>
#pragma gcc optimize('O2')
//#define int long long
#define endl '\n'
using namespace std;

const int N = 1e5 + 10, INF = 0x3f3f3f3f;

struct edge { int u, v, w; const bool operator< (const edge &x) { return w < x.w; }} edges[N];

vector<int> g[N];
int n, k, m;
int pfa[N], dep[N], siz[N], son[N], top[N], dfn[N], rnk[N], ind;
bool del[N];

void dfs1(int u, int fa){
    pfa[u] = fa, dep[u] = dep[fa] + 1, siz[u] = 1;
    for(auto &v : g[u]){
        if(v == fa) continue;
        dfs1(v, u);
        siz[u] += siz[v];
        if(siz[v] > siz[son[u]]) son[u] = v;
    }
}

void dfs2(int u, int tp){
    dfn[u] = ++ind, rnk[tp] = 0, top[u] = tp;
    if(!son[u]) return;
    dfs2(son[u], tp);
    for(auto & v: g[u]){
        if(v == pfa[u] || v == son[u]) continue;
        dfs2(v, v);
    }
}

namespace SegmentTree{
    struct node{
        priority_queue<int, vector<int>, greater<>> val;
        int top(){
            while(val.size() && del[val.top()]) val.pop();
            return val.empty() ? INF : val.top();
        }
    }tree[N << 2];

    #define lson rt << 1, l, mid
    #define rson rt << 1 | 1, mid + 1, r

    void update(int rt, int l, int r, int L, int R, int val){
        if(l >= L && r <= R){
            tree[rt].val.emplace(val);
            return;
        }
        int mid = l + r >> 1;
        if(mid >= L) update(lson, L, R, val);
        if(mid < R) update(rson, L, R, val);
    }

    int query(int rt, int l, int r, int pos){
        if(l == r) return tree[rt].top();
        int mid = l + r >> 1, ans = tree[rt].top();
        if(mid >= pos) ans = min(ans, query(lson, pos));
        else ans = min(ans, query(rson, pos));
        return ans;
    }
}

#define pii pair<int,int> 
#define fir first
#define sec second

void update(int u, int v, int w){
    vector<pii> interval;
    while(top[u] != top[v]){
        if(dep[top[u]] < dep[top[v]]) swap(u, v);
        interval.emplace_back(make_pair(dfn[top[u]], dfn[u]));
        u = pfa[top[u]];
    }
    interval.emplace_back(make_pair(min(dfn[u], dfn[v]), max(dfn[u], dfn[v])));
    sort(interval.begin(), interval.end());
    int modl = 1;
    for(auto & [l, r] : interval){
        int modr = l - 1;
        if(modl <= l - 1) SegmentTree::update(1, 1, n, modl, modr, w);
        modl = r + 1;
    }
    if(modl <= n) SegmentTree::update(1, 1, n, modl, n, w);
}

vector<int> v(1, -0x3f3f3f3f); 

inline void solve(){
    cin >> n >> k >> m;
    
    auto get = [&](int x) { return lower_bound(v.begin(), v.end(), x) - v.begin(); };
    auto dis = [&]() { sort(v.begin(), v.end()); v.erase(unique(v.begin(), v.end()), v.end()); };

    for(int i = 1; i <= n - 1; i++){
        int u, v; cin >> u >> v;
        g[u].emplace_back(v);
        g[v].emplace_back(u);
    }
    dfs1(1, 0), dfs2(1, 1);
    for(int i = 1; i <= k; i++){
        cin >> edges[i].u >> edges[i].v >> edges[i].w;
        v.emplace_back(edges[i].w);
    }
    dis();
    for(int i = 1; i <= k; i++){
        auto &[u, v, w] = edges[i];
        update(u ,v, get(w));
    }
    int min_pos = 1, last = 0;
    for(int i = 1; i <= m; i++){
        int op = 0; cin >> op;
        if(op == 1){
            int x; cin >> x, x = x ^ last;
            int ans = SegmentTree::query(1, 1, n, dfn[x]);
            last = (ans == INF ? -1 : v[ans]);
            cout << last << endl;
        }
        else{
            //auto &[u, v, w] = edges[min_pos++];
            del[min_pos++] = 1;
        }
    }
}

signed main(){
    ios_base::sync_with_stdio(false), cin.tie(0);
    solve();
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

HeartFireY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值