落谷 R94681591 -- 并查集+离线算法+倒序处理

题目描述

树的变迁

思路

因为更改点的权值不会改变树的结构,但是删去一条边会改变树的结构,不同与增加一条边,删除一条边的处理是很麻烦(没实现过!!!)
既然我们无法删除一条边,那么我们可以倒着加边!
这类问题的通用解法是:先保存删边,然后倒叙处理。

犯的错误

1.题目中给定的边的下标从 \(1\) 开始(题目中说的是第 \(k\) 条边,那么 \(k\) 肯定从 \(1\) 开始了),但我代码中边的下标从 \(0\) 开始。不仔细读题的后果)
2.关于节点权值的修改,我们需要一个额外的数组 \(sum\) 来统计树的权值,如果直接在 \(w\) 上修改的话,考虑 \(idx\) 是某个树的 \(root\)(体现在代码中就是并查集的根),如果我们直接修改 \(w[idx]\) ,因为我们的的本意只是修改节点的权值,但是我们把整颗树的权值修改了!那就大错特错了!
3.另外就是由于我们是倒着处理,因此需要倒着输出答案。(倒序处理基本都是需要倒着数处答案的吧!)

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <stack>

using namespace std;

const int N = 1e5 + 10;

int n, m;
int w[N], pre[N], sum[N];
bool st[N];
vector<pair<int,int>> add;
stack<pair<int,pair<int,int>>> query;
stack<int> res;

int find(int x)
{
    if(pre[x] == x) return pre[x];
    return pre[x] = find(pre[x]);
}

void read_init()
{
    for(int i = 0; i < N; i ++ )    pre[i] = i;
    cin >> n >> m;
    for(int i = 1; i <= n; i ++ )   cin >> w[i], sum[i] = w[i];
    for(int i = 0; i < n - 1; i ++ )  {
        int a, b;   cin >> a >> b;
        add.push_back({a,b});
    }
}

void read_query()
{
   for(int i = 0; i < m; i ++ ) {
        int op, a, b;
        cin >> op >> a;
        if(op == 1) { // delete edge
            query.push({1,{a,0}});
            st[a] = true;
        }
        else if(op == 2) { // change edge weight
            cin >> b;
            query.push({2,{a,w[a]}});
            int fa = find(a);
            sum[fa] += b - w[a];
            w[a] = b;
        }
        else { // query weight of tree
            query.push({3,{a,0}});
        }
    } 
}

void add_edge()
{
    for(int i = 0; i < n - 1; i ++ ) {
        if(!st[i + 1]) {
            int a = add[i].first, b = add[i].second;
            int fa = find(a), fb = find(b);
            if(fa != fb) {
                pre[fa] = fb;
                sum[fb] += sum[fa];
            }
        }
    }
}

void solve()
{
    while(query.size()) {
        auto it = query.top();   query.pop();
        int op = it.first, a = it.second.first, b = it.second.second; 
        if(op == 1) { // delete edge
            // now add edge
            int idx = a - 1;
            a = add[idx].first, b = add[idx].second;
            // cout << "a-b: " << a << ' ' << b << endl;
            int fa = find(a), fb = find(b);
            if(fa != fb) {
                pre[fa] = fb;
                sum[fb] += sum[fa];
            }    
        }
        else if(op == 2) { // change edge weight
            int fa = find(a);
            sum[fa] += b - w[a];
            w[a] = b;
        }
        else { // query weight of tree
            res.push(sum[find(a)]);
        }
    }
    while(res.size()) {
        cout << res.top() << endl;
        res.pop();
    }
}

int main()
{
    read_init();
    read_query();
    add_edge();
    solve();
}
posted @ 2022-11-19 19:52  光風霽月  阅读(17)  评论(0编辑  收藏  举报