• 第十一届蓝桥杯C/C++ J题网络分析(带权并查集水题)


    题意:给你n个点,m次操作。操作1是合并a和b;操作2是在p及其连通块内所有点上增加大小为t的值。为你m次操作之后,各个点的大小是多少?

    评测用例及其得分:

    对于 30% 的评测用例,1 ≤ n ≤ 20,1 ≤ m ≤ 100。
    对于 50% 的评测用例,1 ≤ n ≤ 100,1 ≤ m ≤ 1000。
    对于 70% 的评测用例,1 ≤ n ≤ 1000,1 ≤ m ≤ 10000。
    对于所有评测用例,1 ≤ n ≤ 10000,1 ≤ m ≤ 100000,1 ≤ t ≤ 100。

    个人想说的话:本来是不想写题解的,因为没必要,但是网上好像真没有人写出满分正解目前为止,那我就写写吧。

    解法:看到一道题就需要分析复杂度,做题千万不要无脑去莽,很多人上来就是一个add边+bfs暴搜修改权值,这当然可以,但是你只能得70%的分。(最坏复杂度:先1000次操作1建边,最后建到1000个点的双联通图;再9000次操作2bfs,复杂度是O(9000*1000+1000),大概是O(9e6)的复杂度,能过70%样例。)

    但是这种做法在ACM赛制里还是TLE。

    所以我们需要考虑优化,看到这种合并题,第一反应应该就是dsu啊?那我们考虑怎么搞并查集,普通并查集显然8太行(或者维护起来很麻烦),观察性质可以知道,我们只需要维护当前点p及其当前点p的root的差值就可以了。那就好办了啊,带权并查集直接上啊,所以每个点最后的结果就是它与它祖先root的差值+root自身的值就可以了。

    我们对于操作1,那就是非连通块的话合并并且更新其d值,已经是连通块就continue掉。

    我们对于操作2,其实意思就是在他祖先点进行修改就可以了,因为我们要求的最终结果是祖先自己的值+该点与祖先的差值。

    不知道你们懂了没qwq,附上我自己写的代码,欢迎Hack~

    /*
        Author: Anonytt
        Date: 08/08/20 20:35
    */
    
    #include<bits/stdc++.h>
    #pragma GCC optimize(2)
    #define ll long long
    #define rep(i,a,n) for(int i=a;i<=n;i++)
    #define per(i,n,a) for(int i=n;i>=a;i--)
    #define endl '
    '
    #define eps 0.000000001
    #define pb push_back
    #define mem(a,b) memset(a,b,sizeof(a))
    #define IO ios::sync_with_stdio(false);cin.tie(0);
    using namespace std;
    const int INF=0x3f3f3f3f;
    const ll inf=0x3f3f3f3f3f3f3f3f;
    const int mod=1e9+7;
    const int maxn=1e4+5;
    int fa[maxn],d[maxn],now[maxn],n,m;
    int find(int x){
        if(x!=fa[x]){
            int temp=fa[x];
            fa[x]=find(fa[x]);
            d[x]+=d[temp];
        }
        return fa[x];
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) fa[i]=i;
        while(m--){
            int op;scanf("%d",&op);
            if(op==1){
                int a,b;scanf("%d%d",&a,&b);
                int eu=find(a),ev=find(b);
                if(eu!=ev){
                    fa[ev]=eu;
                    d[ev]=now[ev]-now[eu];
                }
            }
            else{
                int p,t;scanf("%d%d",&p,&t);
                int rt=find(p);
                now[rt]+=t;            
            }
        }
        rep(i,1,n){
            printf("%d ",now[find(i)]+d[i]);
        }
        puts("");    
    }
    /*
    4 8
    1 1 2
    2 1 10
    2 3 5
    1 4 1
    2 2 2
    1 1 2
    1 2 4
    2 2 1
    */
    View Code
  • 相关阅读:
    C# 使用HttpWebRequest Post提交数据,携带Cookie和相关参数示例
    关于vue的页面跳转后,如何每次进入页面时都能获取后台数据
    关于vue的页面跳转后,如何每次进入页面时都能获取后台数据
    C#根据汉字获取编码和根据编码获取汉字
    C#根据汉字获取编码和根据编码获取汉字
    多线程系列教材 (一)- Java 创建一个线程的三种方式
    Lambda系列教材 (三)- java 集合的聚合操作
    Lambda系列教材 (二)- 方法引用
    Lambda系列教材 (一)- Java Lambda 表达式教程
    泛型系列教材 (四)- Java 中的子类泛型 转型 成父类泛型
  • 原文地址:https://www.cnblogs.com/Anonytt/p/13460202.html
Copyright © 2020-2023  润新知