[题解]ILY.
简化题意:
给出一张有向图,若干询问,每次询问给出一条边,问加入这条边后有向图中共有多少个强连通分量。
显然本来是强联通分量的子图在加入一条边后还是强联通分量,所以可以先缩点。
缩点后原图变为一张DAG,考虑什么情况下加入边 \(u \to v\) 后强连通分量会减少:
令 \(U,V\) 分别为 \(u,v\) 所属的强联通分量,那么加入边 \(u \to v\) 后强联通分量会减少当且仅当:
-
\(U\) 不可达 \(V\) :加入边 \(u \to v\) 后图的连通性不变
-
\(V\) 可达 \(U\) :加入边 \(u \to v\) 后 \(U \leadsto V\) ,使得在图中增加一个包含 \(U,V\) 的环, \(U,V\) 合并到同一个强联通分量中
综上,加入 \(u \to v\) 后SCC会减少当且仅当 \(U\) 不可达 \(V\) 且 \(V\) 可达 \(U\) 。
这两个条件可以通过传递闭包判断,现在需要解决每次加边一共会减少多少个SCC。
显然,若加入边 \(u \to v\) 后SCC会减少,那么 \(V\) 到 \(U\) 的路径上的所有SCC都会合并为一个SCC,且其他SCC不受影响。因此,若加入边 \(u \to v\) 后SCC减少,则减少的数量为 \(V\) 到 \(U\) 上所有SCC(包括 \(U,V\) )的数量 \(-1\) 。
直接计算这条路径上所有SCC的数量是困难的。不妨考虑从 \(V\) 到 \(U\) 的路径上每个SCC的性质:显然地,某个SCC \(W\) 在 \(V\) 到 \(U\) 的路径上当且仅当 \(V\) 可达 \(W\) 且 \(W\) 可达 \(U\) 。也就是说,设从 \(V\) 出发可以到达的SCC的集合为 \(S_V\) ,可以到达 \(U\) 的SCC的集合为 \(T_U\) ,那么从 \(V\) 到 \(U\) 的路径上的SCC的数量就是 \(|S_V \cap T_U|\) 。
通过传递闭包得出 \(S_V,T_U\) 是容易的。
由于直接传递闭包的时间复杂度是 \(O(n^3)\) 的,所以我们需要bitset优化,时间复杂度为 \(O( \frac{n^3}{ \omega } )\) 。

#include <cstdio>
#include <vector>
#include <bitset>
#include <algorithm>
using namespace std;
struct Edge{int to,next;};
int n,m,q;
vector<vector<int>> ans;
int SCC,cnt_dfn;
vector<int> dfn,low,scc,head;
vector<bool> vis;
vector<Edge> edge;
int top;
vector<int> stack;
vector<bitset<805>> S,T;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}
while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
return x*f;
}
inline void link(const int u,const int v) {edge.push_back({v,head[u]}),head[u]=(int)edge.size()-1;}
inline void tarjan(const int u)
{
dfn[u]=low[u]=++cnt_dfn,stack[++top]=u,vis[u]=true;
for(int i=head[u];i;i=edge[i].next)
{
int v=edge[i].to;
if(!dfn[v]) tarjan(v),low[u]=min(low[u],low[v]);
else if(vis[v]) low[u]=min(low[u],dfn[v]);
}
if(dfn[u]==low[u])
{
SCC++;
while(u^stack[top+1]) scc[stack[top]]=SCC,vis[stack[top]]=false,top--;
}
}
int main()
{
n=read(),m=read(),q=read();
head.resize(n+5),edge.push_back({0});
for(int i=1,u,v;i<=m;++i) u=read(),v=read(),link(u,v);
dfn.resize(n+5),low.resize(n+5),scc.resize(n+5),vis.resize(n+5);
stack.resize(n+5);
for(int u=1;u<=n;++u)
if(!dfn[u]) top=0,tarjan(u);
S.resize(SCC+5);
for(int i=1;i<=n;++i)
for(int j=head[i];j;j=edge[j].next)
{
int u=scc[i],v=scc[edge[j].to];
if(u^v) S[u].set(v);
}
for(int u=1;u<=SCC;++u) S[u].set(u);
for(int k=1;k<=SCC;++k)
for(int u=1;u<=SCC;++u)
if(S[u][k]) S[u]|=S[k];
T.resize(SCC+5);
for(int u=1;u<=SCC;++u)
for(int v=1;v<=SCC;++v)
if(S[u][v]) T[v].set(u);
ans.resize(SCC+5,vector<int>(SCC+5));
for(int u=1;u<=SCC;++u)
for(int v=1;v<=SCC;++v) ans[u][v]=(S[v][u])?(SCC-(S[v]&T[u]).count()+1):(SCC);
while(q--)
{
int u=read(),v=read();
printf("%d\n",ans[scc[u]][scc[v]]);
}
return 0;
}

浙公网安备 33010602011771号