Problem
给出一个有 n n n 个节点, m m m 条边的有向图,询问有多少对点对 ( x , y ) (x,y) (x,y),满足存在两条路径(一条从 1 1 1 到 x x x,一条从 1 1 1 到 y y y),使得这两条路径的交点只有 1 1 1。
数据范围: 1 ≤ n ≤ 1 0 5 1 ≤ n ≤ 10^5 1≤n≤105, 0 ≤ m ≤ 5 × 1 0 5 0 ≤ m ≤ 5 \times 10^5 0≤m≤5×105。
Solution
这道题还是比较妙。
我们先以 1 1 1 为根建一颗支配树,可以发现,当 l c a ( x , y ) = 1 lca(x,y)=1 lca(x,y)=1 时, ( x , y ) (x,y) (x,y) 就是满足条件的点对。
因为如果 l c a ( x , y ) lca(x,y) lca(x,y) 不为 1 1 1,那么这两条路径都必过 l c a ( x , y ) lca(x,y) lca(x,y) 这个点,不满足题目条件。
那么现在的问题就是如何计算 l c a ( x , y ) = 1 lca(x,y)=1 lca(x,y)=1 的点对数量了。
不难看出,只有当 x x x 和 y y y 位于 1 1 1 的不同子树时, l c a ( x , y ) lca(x,y) lca(x,y) 才是 1 1 1。因此我们统计出 1 1 1 的所有子树的 s i z e size size 就可以了。
具体的统计就看代码中的 DfsTree 部分吧。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1000005
using namespace std;
int n,m,tot;
long long ans;
struct edge{
int t,first[N],v[N],nxt[N];
void add(int x,int y){
nxt[++t]=first[x],first[x]=t,v[t]=y;
}
}G,InvG,Semi,Idom;
int Set[N],Min[N],fa[N],dfn[N],pos[N],semi[N],idom[N],Size[N];
int find(int x){
if(Set[x]==x) return x;
int father=find(Set[x]);
if(dfn[semi[Min[Set[x]]]]<dfn[semi[Min[x]]]) Min[x]=Min[Set[x]];
return Set[x]=father;
}
void dfs(int x){
dfn[x]=++tot,pos[tot]=x;
for(int i=G.first[x];i;i=G.nxt[i]){
int to=G.v[i];
if(dfn[to]) continue;
fa[to]=x,dfs(to);
}
}
void Tarjan(){
for(int j=tot;j>1;--j){
int x=pos[j];
for(int i=InvG.first[x];i;i=InvG.nxt[i]){
int to=InvG.v[i];
if(!dfn[to]) continue;
find(to);
if(dfn[semi[Min[to]]]<dfn[semi[x]]) semi[x]=semi[Min[to]];
}
Semi.add(semi[x],x),Set[x]=fa[x];
int now=fa[x];
for(int i=Semi.first[now];i;i=Semi.nxt[i]){
int to=Semi.v[i];find(to);
if(semi[Min[to]]==now) idom[to]=now;
else idom[to]=Min[to];
}
}
for(int i=2;i<=tot;++i){
int x=pos[i];
if(idom[x]!=semi[x]) idom[x]=idom[idom[x]];
Idom.add(idom[x],x);
}
}
void DfsTree(int x){
Size[x]=1;
for(int i=Idom.first[x];i;i=Idom.nxt[i]){
int to=Idom.v[i];DfsTree(to);
if(x==1) ans+=1ll*Size[to]*Size[x];
Size[x]+=Size[to];
}
}
int main(){
int x,y,i;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i)
Set[i]=Min[i]=semi[i]=i;
for(i=1;i<=m;++i){
scanf("%d%d",&x,&y);
G.add(x,y),InvG.add(y,x);
}
dfs(1),Tarjan();
DfsTree(1);
printf("%lld",ans);
return 0;
}