前五题几乎没有什么算法,我们来看一下F题,我写的有点复杂,虽然思路个人感觉还是很清晰的。
F. Non-academic Problem
题意:给你一个无向图,询问能否删除一条边,使可互相到达的点对数量最少。
题解:这道题最大的难点就是这个无向图,如果能将无向图变成树,树上求出点对的数量最小其实还是很简单的,只需要找到子树分割的地方,然后用一点排列组合的知识相加即可。那问题看回来吗,能将无向图变成一颗树怎么做,我们可以发现,如果几个点能互相到达,删除一个边不会对他们产生影响,所以一定不优,那么我们可以将一些环给缩成一个点,然后再次建造一颗树,求解即可,这就需要tarjan缩点,我们先用tarjan把无向图里的环缩成一个点,再次建图跑一边树即可,记得多测一定要清空,代码细节还是蛮多的,希望大家踊跃提问!
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll ;
const int maxn = 2e5 + 7 ;
inline ll read(){
ll x = 0 , f = 1 ;
char c = getchar() ;
while(c > '9' || c < '0'){
if(c == '-')
f = -1;
c = getchar() ;
}
while(c >= '0' && c <= '9'){
x = x * 10 + c - '0' ;
c = getchar() ;
}
return x * f ;
}
ll t , n , m , uu[maxn] , vv[maxn] ;
ll head[maxn] , ver[maxn] , nxt[maxn] , cnt ;
ll ans[maxn] , low[maxn] , dfn[maxn] , tot ;
ll top , st[maxn] , zongsum , an[maxn] ;
void insert(ll x , ll y){
ver[cnt] = y ;
nxt[cnt] = head[x] ;
head[x] = cnt ++;
return ;
}
void tarjan(ll x , ll fa){
dfn[x] = low[x] = ++ tot ;
st[++ top] = x ;
for(int i = head[x] ; ~i ; i = nxt[i]){
ll v = ver[i] ;
if(!dfn[v]){
tarjan(v , i ) ;
low[x] = min(low[v] , low[x]) ;
}
else
if(i != (1 ^ fa)){
low[x] = min(low[x] , dfn[v]) ;
}
}
if(dfn[x] == low[x]){
zongsum ++ ;
ll y ;
do{
y = st[top --] ;
ans[y] = zongsum ;
}while(y != x) ;
}
}
ll Head[maxn] , nxtt[maxn] , Ver[maxn] , cnt1 = 0 , Anss ;
void Insert(ll x , ll y){
++ cnt1 ;
Ver[cnt1] = y ;
nxtt[cnt1] = Head[x] ;
Head[x] = cnt1 ;
}
ll Size[maxn] ;
void dfs(ll x , ll fa){
for(int i = Head[x] ; ~i ; i = nxtt[i]){
ll v = Ver[i] ;
if(v != fa){
dfs(v , x) ;
Size[x] += Size[v] ;
}
}
}
void Dfs(ll x , ll fa){
for(int i = Head[x] ; ~i ; i = nxtt[i]){
ll v = Ver[i] ;
// cout << x << " " << v << endl ;
if(v != fa){
ll rt = Size[1] - Size[v] ;
// cout << rt << " " << Size[v] << endl ;
Anss = min(Anss , (rt * (rt - 1) / 2) + (Size[v] * (Size[v] - 1) / 2)) ;
Dfs(v , x) ;
}
}
}
void solve(){
Anss = LONG_LONG_MAX ;
zongsum = 0 ;
cnt = 0 ;
cnt1 = 0 ;
tot = 0 ;
top = 0 ;
n = read() ;
m = read() ;
for(int i = 0 ; i <= n + 10 ; i ++){
Size[i] = 0 ;
Head[i] = -1 ;
st[i] = 0 ;
head[i] = -1 ;
ans[i] = 0 ;
dfn[i] = 0 ;
low[i] = 0 ;
an[i] = 0 ;
}
for(int i = 1 ; i <= m ; i ++){
ll u , v ;
u = read() ;
v = read() ;
uu[i] = u ;
vv[i] = v ;
insert(u , v) ;
insert(v , u) ;
}
for(int i = 1 ; i <= n ; i ++){
if(!dfn[i])
tarjan(i , i) ;
}
for(int i = 1 ; i <= n ; i ++){
an[ans[i]] ++ ;
}
for(int i = 1 ; i <= m ; i ++){
if(ans[uu[i]] != ans[vv[i]]){
// cout << ans[uu[i]] << " " << ans[vv[i]] << endl ;
Insert(ans[uu[i]] , ans[vv[i]]) ;
Insert(ans[vv[i]] , ans[uu[i]]) ;
}
}
if(cnt1 == 0){
cout << an[1] * (an[1] - 1) / 2 << endl ;
return ;
}
for(int i = 1 ; i <= zongsum ; i ++){
Size[i] = an[i] ;
}
// ll Ans = LONG_LONG_MAX ;
dfs(1 , 0) ;
Dfs(1 , 0) ;
cout << Anss << endl ;
}
int main(){
t = read() ;
while(t --){
solve() ;
}
return 0 ;
}
完美撒花!