zoukankan      html  css  js  c++  java
  • b_aw_信息传递 & 银河英雄传说(并查集暴力求环 / 记忆化 | 带权并查集)

    一、信息传递

    有 n 个同学(编号为 1 到 n)正在玩一个信息传递的游戏。
    在游戏里每人都有一个固定的信息传递对象,其中,编号为 i 的同学的信息传递对象是编号为 Ti 的同学。 
    当有人从别人口中得知自己的生日时,游戏结束。
    请问该游戏一共可以进行几轮?

    输入样例:
    5
    2 4 2 3 1
    输出样例:
    3
    

    方法一:暴力求环

    • 第i个人会把生日告诉a[i],如果你要求环的话,则i的父亲应该定义为a[i],即i指向a[i];为什么?因为如果有环的话,则下一次找的时候一定会有 a[i] 指向 i(i->a[i]->......->i),即当 find(a[i])=i 成立时

    注:此解法不能用路劲压缩

    #include<bits/stdc++.h>
    using namespace std;
    const int N=2e6+5;
    int n, sz, fa[N], ans=N;
    int find(int u) {
        sz++;
        return fa[u]==u ? u : find(fa[u]);
    }
    int main() {
        std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
        cin>>n; for (int i=1; i<=n; i++) fa[i]=i;
        for (int i=1; i<=n; i++) {
            int x; cin>>x, sz=0;
            int fx=find(x);
            if (fx==i) {
                ans=min(ans, sz);
            } else {
                fa[i]=x;
            }
        }
        cout<<ans;
        return 0;
    }
    

    复杂度分析

    • Time\(O(n^2)\),应该是数据比较水
    • Space\(O(n)\)

    方法二:记忆化优化

    
    

    复杂度分析

    • Time\(O()\)
    • Space\(O()\)

    二、银河英雄传说

    有T条指令,每条指令格式为以下两种之一:
    M i j,表示让第i号战舰所在列的全部战舰保持原有顺序,接在第j号战舰所在列的尾部。
    C i j,表示询问第i号战舰与第j号战舰当前是否处于同一列中,如果在同一列中,它们之间间隔了多少艘战舰
    输入 T、op,i,j
    数据范围
    N≤30000,T≤500000

    输入样例:
    4
    M 2 3
    C 1 2
    M 2 4
    C 4 2
    输出样例:
    -1
    1
    

    方法一:并查集

    假设 d[i]表示结点 i 和根节点 root 之间的战舰数量,那么 x、y 之间的战舰数量则为 |d[x]-d[y]|

    #include<bits/stdc++.h>
    using namespace std;
    const int N=3e5+5;
    int fa[N], sz[N], d[N];
    int find(int u) {
        if (fa[u]==u) return u;
        int root=find(fa[u]); //整棵树的根
        d[u]+=d[fa[u]];       //因为路径压缩之后u已经指向根了,所以在更新之前趁早维护d[u]
        return fa[u]=root;
    }
    void merge(int u, int v) {
        int fu=find(u), fv=find(v);
        if (fu!=fv)  {
            fa[fu]=fv;
            d[fu]=sz[fv];
            sz[fv]+=sz[fu];
        }
    }
    int query(int u, int v) {
        int fu=find(u), fv=find(v);
        if (fu!=fv) return -1;
        return abs(d[u]-d[v])-1;
    }
    int main() {
        std::ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
        int t; cin>>t; for (int i=1; i<N; i++) fa[i]=i, sz[i]=1;
        while (t--) {
            char op; 
            int a,b; cin>>op>>a>>b;
            if (op=='M') {
                merge(a,b);
            } else {
                cout<<query(a,b)<<'\n';
            }
        }
        return 0;
    }
    

    类似:https://www.luogu.com.cn/problem/solution/P2294

  • 相关阅读:
    【51NOD 1478】括号序列的最长合法子段
    【BZOJ 3527】【ZJOI 2014】力
    【BZOJ 2194】快速傅立叶之二
    【CodeVS 3123】高精度练习之超大整数乘法 &【BZOJ 2197】FFT快速傅立叶
    【BZOJ 2693】jzptab
    【BZOJ 2154】Crash的数字表格
    【BZOJ 3529】【SDOI 2014】数表
    【BZOJ 2820】YY的GCD
    【BZOJ 2301】【HAOI 2011】Problem b
    【POJ 3294】Life Forms 不小于k个字符串中的最长子串
  • 原文地址:https://www.cnblogs.com/wdt1/p/13641621.html
Copyright © 2011-2022 走看看