题目
题目链接:https://atcoder.jp/contests/arc092/tasks/arc092_d
有一个 (N) 个点 (M) 条边的有向图。保证图中不存在重边和自环。
试判断将每条边反向,其他边不变的情况下,图中强连通分量的数量是否改变。
若改变,输出 diff
,否则输出 same
。
(1 leq N leq 10^3),(1 leq M leq 2 imes 10^5)。
思路
考虑 (u o v) 这条边变向会如何改变强连通分量的数量:
- 如果存在一条 (v o u) 的路径,且变向后不存在 (u o v) 的路径,那么强连通分量会少 (1)。
- 如果存在另一条 (u o v) 的路径,且变向前不存在 (v o u) 的路径,那么强连通分量会多 (1)。
综上,如果强连通分量会改变,当且仅当“存在 (v o u) 的路径”和“存在另一条 (u o v) 的路径”中恰好满足一个。
是否存在 (v o u) 的路径直接 bitset 优化传递闭包就可以了。而存在另一条 (u o v) 的路径,等价于从 (u) 到 (v),(u o v) 这一条边不是必经边。那么把 (u) 的出边按正序和倒序分别枚举一次,各跑 dfs,对于所有点 (v) 记录 (u) 到 (v) 的路径中第一条边的编号(或者到达的点的编号),如果两次记录的编号不同,说明存在另一条 (u o v) 的路径。
时间复杂度 (O(nm+frac{n^3}{omega}))。
代码
#include <bits/stdc++.h>
#define mp make_pair
#define fi first
#define se second
using namespace std;
const int N=1010,M=200010;
int n,m,tot,head[N],pre[N],vis[N],ans[M];
vector<pair<int,int> > e[N];
bitset<N> a[N];
void dfs(int x,int tp,int tag)
{
pre[x]^=tp; vis[x]=tag;
for (int i=0;i<e[x].size();i++)
{
int v=e[x][i].fi;
if (vis[v]!=tag) dfs(v,tp,tag);
}
}
int main()
{
scanf("%d%d",&n,&m);
for (int i=1,x,y;i<=m;i++)
{
scanf("%d%d",&x,&y);
e[x].push_back(mp(y,i)); a[x][y]=1;
}
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
if (a[j][i]) a[j]|=a[i];
for (int i=1;i<=n;i++)
{
memset(pre,0,sizeof(pre));
int lim=e[i].size()-1;
vis[i]=i*2;
for (int j=0;j<=lim;j++) dfs(e[i][j].fi,e[i][j].fi,i*2);
vis[i]=i*2+1;
for (int j=lim;j>=0;j--) dfs(e[i][j].fi,e[i][j].fi,i*2+1);
for (int j=0;j<=lim;j++)
{
int v=e[i][j].fi,id=e[i][j].se;
ans[id]=(pre[v]>0)^a[v][i];
}
}
for (int i=1;i<=m;i++)
ans[i] ? cout<<"diff
" : cout<<"same
";
return 0;
}