ABC285D Change Usernames

\(\texttt{Description}\)

AtCoder Link

洛谷 Link

  • \(n\) 个人,第 \(i\) 个人有名字 \(s_i\) 和理想名字 \(t_i\)。你每次可以将一个人的名字改成理想名字,前提是理想名字目前没人使用。每个人只可以改一次名字。

  • \(1\le n\le 10^5\)\(s_i\ne t_i\)\(s_i\) 互不相同,\(t_i\) 互不相同。

\(\texttt{Solution}\)

结论:将 \(s_i\) 连边向 \(t_i\),若图无环则有解,否则无解。

证明:若图无环,则在拓扑序中存在出度为 \(0\) 的点,这些点是没人占用的。所以可以按照拓扑排序中的层次从后至前依次更改名字。

否则,每个名字都有人占用,没有人可以让出自己的名字,无解。

因此拓扑判环,遍历的点数是否等于总点数(不是 \(n\))即可。

注意字符串用 map 映射一下。

时间复杂度为 \(\mathcal{O}(n\log_2 n)\),空间复杂度为 \(\mathcal{O}(n)\)

\(\texttt{Code}\)

$\texttt{Click Here}$
#include<bits/stdc++.h>
#define N 200005//坑点
using namespace std;
int n,rd[N],tot,has,q[10*N],h=1,t=0;
vector<int>g[N];
map<string,int>mp;
string a,b;
int main(){
    cin>>n;
    for(int i=1;i<=n;++i){
        cin>>a>>b;
        if(!mp[a]){
            mp[a]=++has;
        }
        if(!mp[b]){
            mp[b]=++has;
        }
        g[mp[a]].push_back(mp[b]);
        ++rd[mp[b]];
    }
    for(int i=1;i<=has;++i){
        if(!rd[i]){
            q[++t]=i;
        }
    }
    while(h<=t){
        int u=q[h++];
        ++tot;
        for(int v:g[u]){
            if(!--rd[v]){
                q[++t]=v;
            }
        }
    }
    if(tot^has){
        cout<<"No";
    }else{
        cout<<"Yes";
    }
}
posted @ 2023-01-16 12:38  lzyqwq  阅读(31)  评论(0)    收藏  举报