[题解]P8186 [USACO22FEB] Redistributing Gifts S
P8186 [USACO22FEB] Redistributing Gifts S
对于每行的初始礼物,将它和左侧的礼物连单向边。
最后,每个点都可以通过交换获得所在强连通分量上的任意一个礼物,而其他礼物则无法获得。

可以用 Floyd 跑传递闭包(即判断有向图中两点是否连通)。时间复杂度 \(O(n^3)\) 或 \(O(\dfrac{n^3}{\omega})\)(bitset 优化)。
也可以用 Tarjan 跑一遍强连通。时间复杂度 \(O(n^2)\)。
Floyd 979ms
#include<bits/stdc++.h>
#define eb emplace_back
using namespace std;
const int N=502;
int n,a[N][N],d[N][N];
vector<int> G[N];
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++) cin>>a[i][j];
for(int j=1;j<=n;j++){
if(a[i][j]==i) break;
d[i][a[i][j]]=1;
G[i].eb(a[i][j]);//先记录出边
}
}
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
d[i][j]|=d[i][k]&&d[k][j];
}
}
}
for(int i=1,flg;i<=n;i++){
flg=0;
for(int j:G[i]){
if(d[j][i]){//若出边能回来 说明有环
cout<<j<<"\n";
flg=1;
break;
}
}
if(!flg) cout<<i<<"\n";
}
return 0;
}
Floyd bitset 301ms
#include<bits/stdc++.h>
#define eb emplace_back
using namespace std;
const int N=502;
int n,a[N][N];
bitset<N> d[N];
vector<int> G[N];
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++) cin>>a[i][j];
for(int j=1;j<=n;j++){
if(a[i][j]==i) break;
d[i][a[i][j]]=1;
G[i].eb(a[i][j]);//先记录出边
}
}
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
if(d[i][k]) d[i]|=d[k];
}
}
for(int i=1,flg;i<=n;i++){
flg=0;
for(int j:G[i]){
if(d[j][i]){//若出边能回来 说明有环
cout<<j<<"\n";
flg=1;
break;
}
}
if(!flg) cout<<i<<"\n";
}
return 0;
}
Tarjan 292ms
#include<bits/stdc++.h>
#define eb emplace_back
using namespace std;
const int N=502;
int n,a[N][N],dfn[N],low[N],tim,c[N],idx;
vector<int> G[N];
stack<int> st;
bitset<N> vis;
inline void tarjan(int x){
low[x]=dfn[x]=++tim;
st.push(x),vis[x]=1;
for(int i:G[x]){
if(!dfn[i]) tarjan(i),low[x]=min(low[x],low[i]);
else if(vis[i]) low[x]=min(low[x],dfn[i]);
}
if(dfn[x]==low[x]){
++idx;
while(1){
int t=st.top();
st.pop(),vis[t]=0,c[t]=idx;
if(t==x) break;
}
}
}
signed main(){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++) cin>>a[i][j];
for(int j=1;j<=n;j++){
if(a[i][j]==i) break;
G[i].eb(a[i][j]);
}
}
for(int i=1;i<=n;i++) if(!dfn[i]) tarjan(i);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(c[a[i][j]]==c[i]){
cout<<a[i][j]<<"\n";
break;
}
}
}
return 0;
}
浙公网安备 33010602011771号