Trick or Treat on the Farm(Tarjan缩点题)
题目描述
一张\(n\)个点的有向图,\(n\)个奶牛分别从\(n\)个点出发,每经过一个点答案就\(+1\),直到走到之前走过的点,问答案最大是多少。
思路
1.奶牛回到已经过的点后会停止,那么就是进入到了一个环。
2.见到环要缩点,并记录个个环的大小。
3. 点无非就三种:
(1)在环内的点,它们的\(ans=环的大小\)
(2)在环外的点,它们的\(ans=环的大小+到环的距离\)
(3)环大小为\(1\)时,\(ans=1\)
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxn=1e5+5;
ll n,cnt,num,rt,dfn[maxn],low[maxn],scc[maxn],val[maxn],ans[maxn];
vector<ll> e[maxn];
map<pair<ll,ll>,bool> mp;
stack<ll> stk;
bool isin[maxn];
void dfs1(ll x){
dfn[x]=low[x]=++cnt;
isin[x]=true;
stk.push(x);
for(auto y:e[x]){
if(!dfn[y]){
dfs1(y);
low[x]=min(low[x],low[y]);
}else if(isin[y]){
low[x]=min(low[x],dfn[y]);
}
}
if(dfn[x]==low[x]){
num++;
ll y;
do{
y=stk.top();
stk.pop();
scc[y]=num;
isin[y]=false;
}while(y!=x);
}
}
void dfs2(ll x,ll step){
for(auto y:e[x]){
if(ans[y]==0){
dfs2(y,step+1);
}else{
ans[rt]=ans[y]+step;
return;
}
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(nullptr);
cin>>n;
for(ll i=1;i<=n;i++){
ll x;
cin>>x;
if(i==x){
ans[i]=1;
}else if(!mp[{i,x}]){
mp[{i,x}]=true;
e[i].push_back(x);
}
}
for(ll i=1;i<=n;i++){
if(!dfn[i]){
dfs1(i);
}
}
for(ll i=1;i<=n;i++){
val[scc[i]]++;
}
for(ll i=1;i<=n;i++){
if(val[scc[i]]!=1){
ans[i]=val[scc[i]];
}
}
for(ll i=1;i<=n;i++){
if(ans[i]==0){
rt=i;
dfs2(i,1);
}
}
for(ll i=1;i<=n;i++){
cout<<ans[i]<<"\n";
}
return 0;
}

浙公网安备 33010602011771号