• 并查集(我根本不会切板子啊喂QWQ长文)(大雾


    说句实话,我和并查集的缘分还是蛮深的,因为当年学完数论想着找板子题乱做(真是个神奇的找题方式呢),然后就看到了并查集QWQ,看了一会发现是图论就不看了,,,,,,结果还被说是大佬QWQ其实我只是个NaCl Fish而已QAQ

    好了现在终于学了并查集,那我们就来总结总结

    这一次总共是有三道题要讲

    首先我们来看看板子题

    P3367 【模板】并查集

    并查集,在一些有N个元素的集合应用问题中,我们通常是在开始时让每个元素构成一个单元素的集合,然后按一定顺序将属于同一组的元素所在的集合合并,其间要反复查找一个元素在哪个集合中。这一类问题近几年来反复出现在信息学的国际国内赛题中,其特点是看似并不复杂,但数据量极大,若用正常的数据结构来描述的话,往往在空间上过大,计算机无法承受;即使在空间上勉强通过,运行的时间复杂度也极高,根本就不可能在比赛规定的运行时间(1~3秒)内计算出试题需要的结果,只能用并查集来描述。 (摘自百度)

    所谓并查集,其实就是并       查         集        这一点看题面就能理解了

    这里看一下   并(把两个集合合并到一起)

    void merge(int a, int b)
    {
        father[search(a)]=search(b);
    }

    这里看一下    查(查一个点的祖宗是啥)

    int search(int a)
    {
        if (father[a] == a)
            return a;
        return father[a] = search(father[a]);
    }

    这里形象的理解一下(某谷题解)

    关于并查集和路径压缩:

    有a,b,c三个人

    假设a和b打架了,a做了b的小弟。则令f[a]=b;

    后来a打赢了c

    那么c就是a的小弟了。所以,令f[c]=a;

    但是,c不知道b,这不符合要求。

    所以,我们必须让c的大哥变成最大的老大。

    这个就是查的过程

    int search(int a)
    {
        if (father[a] == a)
            return a;
        return father[a] = search(father[a]);
    }

    这里我用了一个比较好的优化技巧,就是在找一个点的祖宗的时候,一块把所有经过的点的祖宗都进行标记,这样就比较快了,看一下代码的话,也是赋了一个递归函数的返回值。这样的话找爹就更容易点了

    代码贴一下

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    int n, m, x, y, z, father[10010], t1, t2;
    int search(int a)
    {
        if (father[a] == a)
            return a;
        return father[a] = search(father[a]);
    }
    void merge(int a, int b)
    {
        father[search(a)]=search(b);
    }
    int main()
    {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n; ++i)
            father[i] = i;
    
        for (int i = 1; i <= m; ++i)
        {
            scanf("%d%d%d", &z, &x, &y);
            if (z == 1)
                merge(x, y);
            else
            {
                if (search(x) == search(y))
                    printf("Y
    ");
                else
                    printf("N
    ");
            }
        }
        return 0;
    }

    那么我们看下一个题

    P1551 亲戚

    这个题吧其实也算得上是一道并查集的板子题了,主要的就是分析一下要你干什么

    我们来看看

    ,首先,我们假设每一个人都是一个独立的集合,在他输入两个人之间是亲戚关系的时候,我们就需要把这两个人所在的集合合并了,还是用到了上面的代码

    这里边读入边合并,最后直接输出找爹结果就行了(还是挺水的)

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    int n, m, x, y, z, father[10010], p;
    int search(int a)
    {
        if (father[a] == a)
            return a;
        return father[a] = search(father[a]);
    }
    void merge(int a, int b)
    {
        father[search(a)] = search(b);
    }
    int main()
    {
        scanf("%d%d%d", &n, &m, &p);
        for (int i = 1; i <= n; ++i)
            father[i] = i;
        for (int i = 1; i <= m; ++i)
        {
            scanf("%d%d", &x, &y);
            merge(x, y);
        }
        for (int i = 1; i <= p; ++i)
        {
            scanf("%d%d", &x, &y);
            if (search(x) == search(y))
                printf("Yes
    ");
            else
                printf("No
    ");
        }
        return 0;
    }

    最后一个题是这货

    P3984 高兴的津津

    这个题的标签是这样的

    所以蒟蒻我一开始只是用数学方法做的啊QWQ

    讲讲数学加模拟的实现吧

    首先我们知道津津AKIOI之后会开心t天,但是在这t天以内,如果她再次AK,那么时间从头算起,这样的话,我们就可以比较每两个数的差,看是否大于t并且进行运算啦

    贴代码,跑路~

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    int n, t, a[200010], ans;
    int main()
    {
        scanf("%d%d", &n, &t);
        for (int i = 1; i <= n; ++i)
        {
            scanf("%d", &a[i]);
            if (i != 1)
            {
                if (a[i] - a[i - 1] > t)
                    ans += t;
                else
                    ans += a[i] - a[i - 1];
            }
        }
        ans += t;
        printf("%d", ans);
        return 0;
    }
  • 相关阅读:
    @Order
    uri和url , 以及末尾加不加 '/'
    windows删除右键新建多余选项
    Typora 指南
    常见状态码/HttpStatusCode
    final 修饰符
    4. shiro-整合redis
    springboot 整合mybatis 一级缓存失效
    springboot查看具体访问的url片段
    JavaSE:NIO
  • 原文地址:https://www.cnblogs.com/this-is-M/p/10765104.html
Copyright © 2020-2023  润新知