原题链接:B. Two Fairs
题目大意:
给出一张有 n 个结点、m 条边的无向联通图,同时给出a,b两个特殊点,询问图上有多少对点满足从一个点到另一个点都必须经过这两个重要点,即求出任意一条从u到v的路径都经过a和b的二元组 (u,v) 的对数
数据范围:1≤t≤4⋅104,4≤n≤2⋅105 , n−1≤m≤5⋅105, 1≤a,b≤n, a≠b,1≤ui,vi≤n , ui≠vi
测试中所有输入数据的n之和不超过2⋅105.测试中所有输入数据的m值总和不超过5⋅105
解题思路:
首先,给出一个图,我们可以先思考哪些点u是从u走到b必须经过点a的,
显然图中标红的点走到b点是必须经过a点的,那么如何找到这些点呢?
我们可以从b点开始进行dfs,碰到a点就返回,剩下未被标记的点就是走到b点是必须经过a点的点
void dfsb(int x)
{
if(visb[x]||x==a)return;
visb[x]=1;
nb++;
for (int i=0;i<ed[x].size();i++)
{
int p=ed[x][i];
if (visb[p]) continue;
dfsb(p);
}
}
标记结果如下图:
假设被标记的点的数量为na,那么未被标记的点的数量为n-na,注意a点是不能选中的,所以满足要求的点为n-na-1
对点B分析时用相同的操作,可得到满足要求的点数量为n-nb-1,最后用乘法原理即可得出答案
cout<<(n-na-1)*(n-nb-1)<<'\n';
AC代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
int visa[200005],visb[200005];
vector<int> ed[600005];
int n,m,a,b;
int na,nb;
void dfsa(int x)
{
if(visa[x]||x==b)return;
visa[x]=1;
na++;
for (int i=0;i<ed[x].size();i++)
{
int p=ed[x][i];
if (visa[p]) continue;
dfsa(p);
}
}
void dfsb(int x)
{
if(visb[x]||x==a)return;
visb[x]=1;
nb++;
for (int i=0;i<ed[x].size();i++)
{
int p=ed[x][i];
if (visb[p]) continue;
dfsb(p);
}
}
void solve(){
cin>>n>>m>>a>>b;
for(int i=1;i<=n;i++)ed[i].clear();
for(int i=1;i<=n;i++)
{
visa[i]=0;
visb[i]=0;
}
//建图
for(int i=1;i<=m;i++)
{
int u,v;
cin>>u>>v;
ed[u].push_back(v);
ed[v].push_back(u);
}
na=0,nb=0;
dfsa(a);
dfsb(b);
//乘法原理
cout<<(n-na-1)*(n-nb-1)<<'\n';
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0);cout.tie(0);
int t=1;
cin>>t;
while(t--)solve();
return 0;
}