• [NOIP2015] D1T2 信息传递


    洛谷题目链接:https://www.luogu.org/problemnew/show/2661

    一道有很多种解法的题目

    通过划归,发现就是求最小环

    那么立即能想到的算法:1、Tarjan求强连通分量,最无脑

                                            2、对于每个连通分量用Topo Sort,相当于剪去其他不在环上的边

    不过用带权并查集也可以解决这道题目

    首先发现只要一条边连接的两个点A、B在之前已经在一个集合中,则必定会形成一个环

    那么难点就在于如何每次高效地求出环的大小

    确实可以维护每个点到其根节点的距离,但每次merge操作要改变很多点的值

    于是我们可以每次merge时只维护其到父节点的距离,不用有其他操作

    而在find时再通过递归求出这个点到根节点的距离

    Ex:有3个点A、B、C,f[A]=B,f[B]=C。在find前d[A]=cost(A,B),d[B]=cost(B,C),在递归时仅要d[A]+=d[f[A]]即可。

    可以发现这样的处理和线段树中的lazy_tag运用了同样的思想

    为了降低复杂度(只要不影响其他值的查找)在修改操作时不用对可能受影响的点全部进行修改,只维护最近点的数据,在查找操作时统一操作即可

    Tips:1、在这样进行维护时,合并操作不能使用启发式合并,因为在查找时必须保证一条边两个节点间的父子关系不发生改变

       2、n个点n-1条边绝不意味着只可能出现一个环,很可能有多个连通分量,从而出现多个环

     1 #include <bits/stdc++.h>
     2 
     3 using namespace std;
     4 int n,f[200005],d[200005];
     5 
     6 int find(int x)
     7 {
     8     if(f[x]!=x)  //对d进行维护 
     9     {
    10         int root=find(f[x]);
    11         d[x]+=d[f[x]];
    12         return f[x]=root;
    13     }
    14     else return x;
    15 }
    16 
    17 int main()
    18 {
    19     cin >> n;
    20     for(int i=1;i<=n;i++) f[i]=i,d[i]=0;
    21     
    22     int res=1<<27;
    23     for(int i=1;i<=n;i++)
    24     {
    25         int x;cin >> x;
    26         int px=find(x),py=find(i);
    27         
    28         if(px==py) res=min(res,d[i]+d[x]+1);
    29         else f[i]=x,d[i]=1; //只能使用朴素合并方式 
    30     }
    31     cout << res;
    32     
    33     return 0;
    34 }
  • 相关阅读:
    死磕java(3)
    死磕java(2)
    死磕java(1)
    开源 android
    android开发:点击缩略图查看大图
    android java获取当前时间的总结
    Android多屏幕适配
    Android-关于屏幕适配的一些经验
    Android TextView自动换行文字排版参差不齐的原因
    proguard.cfg 配置文件
  • 原文地址:https://www.cnblogs.com/newera/p/7905631.html
Copyright © 2020-2023  润新知