传送门:https://www.luogu.org/problemnew/show/P2661
这个是一个图的遍历问题,主要是2和7个点被卡了
类似于tarjan,我们在dfs的时候遍历到那个节点,用一个时间戳记录那个节点被搜索到的顺序,因为每次搜索一定会存在环,那么搜索到时间戳已经被记录的节点的时候,时间戳的差+1就是我们这个环的大小。
但是这个做法是可以被卡掉的,因为为了下一次搜素,我们在搜索完一个之后要把时间戳清空,那么假设我们现在搜索完i节点的时候,下一个搜索的是指向i节点的一个节点,那么i节点,以及i节点以下之前搜索过的部分都会被重新搜索,那么假设是一条链的样子,最后三个节点成环,每一个i+1节点都指向的是i节点,那么我们在搜索的时候,这个方法的复杂度就达到了n^2所以我们可以用并查集来记录已经搜索过的集合,一个集合里只可能有一个环,那么搜索完之后标记这个集合避免重复搜索,这样复杂度基本就可以稳定在O(n)
代码如下:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int gcd(int a,int b){if (b == 0) return a; return gcd(b , a%b);}
int lcm(int a, int b){ return a/gcd(a,b)*b;}
inline int read(){
int f = 1, x = 0;char ch = getchar();
while (ch > '9r' || ch < '0'){if (ch == '-')f = -f;ch = getchar();}
while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
return x * f;
}
const int maxn = 2e5 + 10;
int nex[maxn],vis[maxn],fa[maxn],used[maxn],cnt = 1,ans = INT_MAX;
int find(int x){
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void join(int x,int y){
int fx = find(x),fy = find(y);
if (fx != fy) {
fa[fx] = fy;
}
}
void dfs(int u){
int now = u,pre = now;
cnt = 1;
used[find(u)] = 1;
while(1){
if (vis[now]){
ans = min(ans,vis[pre]-vis[now]+1);
while(u != pre){
vis[u] = 0;
u = nex[u];
}
vis[u] = 0;
return;
}
vis[now] = cnt++;
pre = now;
now = nex[now];
}
}
int main(){
int n = read();
for(int i=1;i<=n;i++) fa[i] = i;
for (int i = 1; i <= n; ++i) {
nex[i] = read();
join(i,nex[i]);
}
for (int i = 1; i <= n; ++i) {
int fx = find(i);
if (!used[fx]) {
dfs(i);
}
}
cout << ans << endl;
return 0;
}