洛谷 P2661 信息传递

本文探讨了一种利用并查集优化图遍历算法的方法,解决在特定情况下传统DFS算法的时间复杂度问题。通过记录节点的搜索状态,使用并查集避免重复搜索,将复杂度稳定在O(n),适用于解决包含多个环的图问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门: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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值