20221113_T1A-_并查集

题意

在一个文件目录下有 \(n\) 个不同的文件,每个文件都有一个文件名,其中第 \(i\) 个文
件的文件名为 \(s_i\)。这些文件名两两不同。
小 C 希望更改一部分文件的文件名,他对于每个文件都拟定了它的目标文件名,
其中第 \(i\) 个文件的目标文件名为 \(t_i\)。同样地,所有目标文件名也是两两不同的。
为了更改文件名,小 C 需要进行若干次的重命名操作。每次重命名,小 C 可以选
一个文件,将其文件名改成任意字符串。不过,重命名要求遵循这样的规则:每次重命
名之后,所有这 \(n\) 个文件的文件名仍应两两不同。
那么,小 C 至少要进行多少次重命名操作,才能使所有文件名都改成相应的目标
文件名呢?

题解

赛时得分:100/100

非常显然,如果遇到了环那么答案就 +1,如果这个环是环是自环那么答案 -1,一条链那么对答案没有影响(意思是交换就行了)。

现在就是怎么判环的问题了。并不难,我们直接并查集,如果查到两个东西本来就在一个集合里了,那么说明有环了。

代码

#include <bits/stdc++.h>
using namespace std;
template <typename T>inline void read(T& t){t=0; register char ch=getchar(); register int fflag=1;while(!('0'<=ch&&ch<='9')) {if(ch=='-') fflag=-1;ch=getchar();}while(('0'<=ch&&ch<='9')){t=t*10+ch-'0'; ch=getchar();} t*=fflag;}
template <typename T,typename... Args> inline void read(T& t, Args&... args) {read(t);read(args...);}
const int N = 1e5 + 10, inf = 0x3f3f3f3f;

int n, cnt, fa[N], ans;
map<string, int>mp;
string st[N], st1[N];
int findf(int x) {
    if(fa[x] == x) return x;
    else return fa[x] = findf(fa[x]);
}

int main() {
    freopen("files.in", "r", stdin);
    freopen("files.out", "w", stdout);
    read(n);
    for(int i = 1; i <= n; ++i) fa[i] = i;
    for(int i = 1; i <= n; ++i) {
        cin >> st[i] >> st1[i];
        if(!mp.count(st1[i])) mp[st1[i]] = i;
    }
    for(int i = 1; i <= n; ++i) {
        if(st[i] == st1[i]) {
            --ans;
            continue;
        }
        if(mp.count(st[i])) {
            int k = i, l = mp[st[i]];
            if(findf(k) == findf(l)) {
                // cout << k << ' ' << l << endl;
                ++ans;
            } else fa[findf(l)] = findf(k);
        }
    }
    cout << ans + n << endl;
    return 0;
}
posted @ 2022-11-13 21:46  Mercury_City  阅读(23)  评论(0编辑  收藏  举报