蓝桥杯2020.7(C++ B组) 网络分析

题目链接

思路

一开始想着直接并查集,然后开个结果数组,每次传递信息,都假设这个信息是从父节点开始传递的,让父节点的结果数组加上发送的消息数量就可以,然后子节点的信息数,就是子节点的结果集加上父节点的结果集,就像下面,假设有两个点(1,2),进行两个操作,将1和2连接,在2上发送信息

这个时候就会有一个问题,假设在1还没有和2连接的时候就发送了消息,然后再连接,此时查询结果,会产生2没连在1上时发送的消息也被算在2内的现象,即:

很明显,结果应该为 1 和 0 ,但是依照之前的思路结果却为 1 和 1 ,此时我们考虑连上一条边,就把这条边先减去父节点消息数,有点差分的意思,像下图

此时结果就正确了。非常自信的认为自己正确了,自信交卷准备一发入魂,结果就看到了
WRONG ANSWER

开始怀疑人生,到底是哪里出了问题,跑去看了一下测试点,手动用草稿纸模拟了一边,终于让我发现了问题所在
如果连接的两个点,本身自身就带着一堆的子节点,两个父节点相连,让连上去的父节点减去最终的父节点的值,连上去的父节点没问题,但是它的所有子节点都没有去减掉父节点的值,类似下图

发现问题所在,那么接下来就是解决问题了,跑去上了个厕所,灵感喷涌而出,都说厕所很有灵感果然是真的(。。。)
我们在每一次加上点的时候,都先把被加上点的所有子节点都去减去最终父节点的值,同时加上被加上点的值就可以了,只需要开一个辅助的vector就能完美的完成这件事,思路清奇,非常完美。

代码

#include<iostream>
#include<vector>
using namespace std;

int father[10005];

int result[10005];
vector<int> v[10005];
void init(int n){
    for(int i = 1;i <= n;i++)
        father[i] = i;
}

int find(int x){
    return x == father[x] ? x : father[x] = find(father[x]);
}

void unite(int x , int y){
    int a = find(x);
    int b = find(y);
    int m = min(a , b);
    int ma = max(a , b);
    father[a] = m;
    father[b] = m;
    int len = v[ma].size();
    v[m].push_back(ma);
    for(int i = 0; i < len ;i++){
        v[m].push_back(v[ma][i]);
        result[v[ma][i]] -= result[m];
        result[v[ma][i]] += result[ma];
    }
    result[ma] -= result[m];
    v[ma].clear();
}

int main(){
    int n , m;
    cin >> n >> m ;
    init(n);
    for(int i = 1;i <= m;i++){
        int op , p , t;
        cin >> op >> p >> t;
        if(op == 1){
            if(find(p) == find(t))
                continue;
            unite(p , t);
        }else{
            int x = find(p);
            result[x] += t;
        }
    }
    for(int i = 1;i <= n;i++){
        int x = find(i);
        if(x == i){
            cout << result[i] << " ";
        }else{
            cout << result[i] + result[x] << " ";
        }
    }
    cout << endl;
    return 0;
}

终于一道简单题 ACCEPT

posted @ 2020-10-12 21:58  zengbiaojie  阅读(370)  评论(1)    收藏  举报