[NOIP2015] D1T2 信息传递

Posted NewErA

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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 }

 

以上是关于[NOIP2015] D1T2 信息传递的主要内容,如果未能解决你的问题,请参考以下文章

NOIP2015信息传递

NOIP2015 信息传递

2105. [NOIP2015] 信息传递

NOIP 2015:信息传递

[noip2013 d1t2]火柴排队

[noip2011 d1t2]选择客栈