无向图上割点/割边
无向图上割点/割边
下面的四个算法都是针对无向图的
割点
注意:割点不判断前一个边(判断也行) 割边一定需要判断前一个边
解释:对于
但是割边则不同 如果你让
P3388 【模板】割点(割顶)
- 如果一个点
为根 且有至少两个子树(这两个子树必须是两个独立的子树 即互相之间不能到达) 那么是割点 - 如果一个点
不是根 且 说明子节点 不能通过非树边到达 以上的节点 则必然可以通过割掉 来使得 不能和 前面的点连通 那么是割点
在
还需要注意:一个点作为割点可能会被标记多次 我们只需要标记 在最后统计即可 或者可以用
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define mid (l+r>>1)
#define inl inline
#define eb emplace_back
#define pii pair<int,int>
#define fi first
#define se second
const int N = 2e4 + 5;
const int inf = 0x3f3f3f3f;
char buf[1<<24] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
// #define getchar() cin.get();
int read()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f ;
}
int n , m , pd[N] , cnt;
vector<int> e[N];
inl void add ( int u , int v ) { e[u].eb(v); }
int dfn[N] , low[N] , timer;
void tarjan ( int u , int rt )
{
dfn[u] = low[u] = ++timer;
int child = 0;
for ( auto v : e[u] )
{
if ( !dfn[v] )
{
tarjan ( v , rt ) , low[u] = min ( low[u] , low[v] );
if ( low[v] >= dfn[u] && u != rt ) pd[u] = 1;
++ child;
}
else low[u] = min ( low[u] , dfn[v] );
}
if ( u == rt && child >= 2 ) pd[u] = 1;
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(nullptr) , cout.tie(nullptr);
n = read() , m = read();
for ( int i = 1 , u , v ; i <= m ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
for ( int i = 1 ; i <= n ; i ++ ) if ( !dfn[i] ) tarjan(i,i);
for ( int i = 1 ; i <= n ; i ++ ) if ( pd[i] ) cnt ++;
cout << cnt << endl;
for ( int i = 1 ; i <= n ; i ++ ) if ( pd[i] ) cout << i << ' ';
return 0;
}
P3469 [POI2008] BLO-Blockade
显然对于每一个割点 如果删去它形成的连通块大小分别为
(我们对于
如果不是割点 那么答案即为
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define mid (l+r>>1)
#define inl inline
#define eb emplace_back
#define pii pair<int,int>
#define fi first
#define se second
#define int long long
const int N = 1e5 + 5;
// char buf[1<<24] , *p1 , *p2;
// #define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
#define getchar() cin.get();
int read()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f ;
}
int n , m , ans[N] , cut[N] , sz[N];
vector<int> e[N];
inl void add ( int u , int v ) { e[u].eb(v); }
int dfn[N] , low[N] , timer;
void tarjan ( int u , int rt )
{
int child = 0 , sum = 0;
dfn[u] = low[u] = ++ timer , sz[u] = 1;
for ( auto v : e[u] )
if ( !dfn[v] )
{
tarjan ( v , rt ) , low[u] = min ( low[u] , low[v] ) , sz[u] += sz[v];
if ( dfn[u] <= low[v] )
{
ans[u] += ( n - sz[v] - 1 ) * sz[v] , sum += sz[v];
if ( u != rt ) cut[u] = 1;
}
++ child;
}
else low[u] = min ( low[u] , dfn[v] );
if ( u == rt && child >= 2 ) cut[u] = 1;
if ( cut[u] ) ans[u] += sum * ( n - sum - 1 );
ans[u] += 2 * ( n - 1 );
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(nullptr) , cout.tie(nullptr);
n = read() , m = read();
for ( int i = 1 , u , v ; i <= m ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
for ( int i = 1 ; i <= n ; i ++ ) if ( !dfn[i] ) tarjan ( i , i );
for ( int i = 1 ; i <= n ; i ++ ) cout << ans[i] << endl;
return 0;
}
P5058 [ZJOI2004] 嗅探器
为了简化题目 我们的
这里第二个柿子是因为我们现在看的是割掉
那么
这样求出所有割点即可
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define mid (l+r>>1)
#define inl inline
#define eb emplace_back
#define pii pair<int,int>
#define fi first
#define se second
const int N = 2e5 + 5;
//char buf[1<<24] , *p1 , *p2;
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
#define getchar() cin.get();
int read()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f ;
}
int n , uu , vv , cut[N];
vector<int> e[N];
inl void add ( int u , int v ) { e[u].eb(v); }
int dfn[N] , low[N] , timer;
void tarjan ( int u , int rt )
{
dfn[u] = low[u] = ++timer;
for ( auto v : e[u] )
{
if ( !dfn[v] )
{
tarjan(v,rt) , low[u] = min ( low[u] , low[v] );
if ( low[v] >= dfn[u] && u != rt && dfn[vv] >= dfn[v] ) cut[u] = 1;
}
else low[u] = min ( low[u] , dfn[v] );
}
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(nullptr) , cout.tie(nullptr);
n = read();
while ( ( uu = read() ) + ( vv = read() ) != 0 ) add ( uu , vv ) , add ( vv , uu );
uu = read() , vv = read();
tarjan(uu,uu);
for ( int i = 1 ; i <= n ; i ++ ) if ( cut[i] ) { cout << i << endl; return 0; }
return cout << "No solution" << endl , 0;
}
割边
相当于在割点板子上改了一下判断条件
这样是为了防止搜索
P1656 炸铁路
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define mid (l+r>>1)
#define inl inline
#define eb emplace_back
#define pii pair<int,int>
#define fi first
#define se second
const int N = 1e5 + 5;
char buf[1<<24] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
// #define getchar() cin.get();
int read()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f ;
}
int n , m , k , l , sza[N] , szb[N] , cnt = 1;
vector<pii> e[N] , ans;
inl void add ( int u , int v , int w ) { e[u].eb(v,w); }
int low[N] , dfn[N] , timer;
void tarjan ( int u , int ff )
{
dfn[u] = low[u] = ++timer;
for ( auto [v,p] : e[u] )
{
if ( !dfn[v] )
{
tarjan ( v , p ) , low[u] = min ( low[u] , low[v] ) , sza[u] += sza[v] , szb[u] += szb[v];
if ( low[v] > dfn[u] && ( sza[v] == 0 || szb[v] == 0 || sza[v] == k || szb[v] == l ) ) ans.eb(u,v);
}
else if ( p ^ ( ff ^ 1 ) ) low[u] = min ( low[u] , dfn[v] );
}
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(nullptr) , cout.tie(nullptr);
n = read() , m = read() , k = read() , l = read();
for ( int i = 1 ; i <= k ; i ++ ) sza[read()] = 1;
for ( int i = 1 ; i <= l ; i ++ ) szb[read()] = 1;
for ( int i = 1 , u , v ; i <= m ; i ++ ) u = read() , v = read() , add ( u , v , ++ cnt ) , add ( v , u , ++ cnt );
tarjan(1,0);
cout << ans.size() << endl;
for ( auto p : ans ) cout << p.fi << ' ' << p.se << endl;
return 0;
}
P7687 [CEOI2005] Critical Network Lines
割边板子
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define mid (l+r>>1)
#define inl inline
#define eb emplace_back
#define pii pair<int,int>
#define fi first
#define se second
const int N = 1e5 + 5;
char buf[1<<24] , *p1 , *p2;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<24,stdin),p1==p2)?EOF:*p1++)
// #define getchar() cin.get();
int read()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f ;
}
int n , m , k , l , sza[N] , szb[N] , cnt = 1;
vector<pii> e[N] , ans;
inl void add ( int u , int v , int w ) { e[u].eb(v,w); }
int low[N] , dfn[N] , timer;
void tarjan ( int u , int ff )
{
dfn[u] = low[u] = ++timer;
for ( auto [v,p] : e[u] )
{
if ( !dfn[v] )
{
tarjan ( v , p ) , low[u] = min ( low[u] , low[v] ) , sza[u] += sza[v] , szb[u] += szb[v];
if ( low[v] > dfn[u] && ( sza[v] == 0 || szb[v] == 0 || sza[v] == k || szb[v] == l ) ) ans.eb(u,v);
}
else if ( p ^ ( ff ^ 1 ) ) low[u] = min ( low[u] , dfn[v] );
}
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(nullptr) , cout.tie(nullptr);
n = read() , m = read() , k = read() , l = read();
for ( int i = 1 ; i <= k ; i ++ ) sza[read()] = 1;
for ( int i = 1 ; i <= l ; i ++ ) szb[read()] = 1;
for ( int i = 1 , u , v ; i <= m ; i ++ ) u = read() , v = read() , add ( u , v , ++ cnt ) , add ( v , u , ++ cnt );
tarjan(1,0);
cout << ans.size() << endl;
for ( auto p : ans ) cout << p.fi << ' ' << p.se << endl;
return 0;
}
点双
点双/边双针对无向图
在
P8435 【模板】点双连通分量
代码中的特判是针对孤立点的() 如果我们想要用有没有边来判断孤立点的话 记得需要去重边
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define mid (l+r>>1)
#define inl inline
#define eb emplace_back
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define getchar() cin.get();
const int N = 5e5 + 5;
int read()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f ;
}
int n , m , mod , a[N] , tot;
vector<int> e[N] , bcc[N];
inl void add ( int u , int v ) { e[u].eb(v); }
int low[N] , dfn[N] , timer;
int sta[N] , top;
void tarjan ( int u , int rt )
{
low[u] = dfn[u] = ++timer;
sta[++top] = u;
for ( auto v : e[u] )
if ( !dfn[v] )
{
tarjan ( v , rt ) , low[u] = min ( low[u] , low[v] );
if ( dfn[u] <= low[v] )
{
++ tot;
while ( top )
{
int x = sta[top--]; bcc[tot].eb(x);
if ( x == v ) break;
}
bcc[tot].eb(u);
}
}
else low[u] = min ( low[u] , dfn[v] );
if ( u == rt && !e[u].size() ) bcc[++tot].eb(u);
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(nullptr) , cout.tie(nullptr);
n = read() , m = read();
for ( int i = 1 , u , v ; i <= m ; i ++ )
{
u = read() , v = read();
if ( u != v ) add ( u , v ) , add ( v , u );
}
for ( int i = 1 ; i <= n ; i ++ ) if ( !dfn[i] ) tarjan ( i , i );
cout << tot << endl;
for ( int i = 1 ; i <= tot ; i ++ )
{
cout << bcc[i].size() << ' ';
for ( auto v : bcc[i] ) cout << v << ' ';
cout << endl;
}
return 0;
}
边双
相当于是将题中所有的割边都去掉 形成的联通块 那么开一个栈来进行类
而且 我们可能加了两条及以上
所以我们需要记录进边的值来判断是否向下搜索
P8436 【模板】边双连通分量
#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
#define inl inline
#define eb emplace_back
#define endl '\n'
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define print(x) cerr<<#x<<'='<<x<<endl
#define getchar() cin.get()
const int N = 5e5 + 5;
int read()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , m , sta[10000000] , top , cnt = 1;
vector<pii> e[N];
vector<int> bcc[N];
inl void add ( int u , int v , int w ) { e[u].eb(v,w); }
int tot , dfn[N] , low[N] , timer;
void tarjan ( int u , int ff )
{
low[u] = dfn[u] = ++timer;
sta[++top] = u;
for ( auto [v,k] : e[u] )
{
if ( !dfn[v] ) tarjan ( v , k ) , low[u] = min ( low[u] , low[v] );
else if ( k != ( ff ^ 1 ) ) low[u] = min ( low[u] , dfn[v] );
}
if ( low[u] == dfn[u] )
{
++ tot;
while ( top )
{
int x = sta[top--]; bcc[tot].eb(x);
if ( x == u ) break;
}
}
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read();
for ( int i = 1 , u , v ; i <= m ; i ++ ) u = read() , v = read() , add ( u , v , ++ cnt ) , add ( v , u , ++ cnt );
for ( int i = 1 ; i <= n ; i ++ ) if ( !dfn[i] ) tarjan ( i , 0 );
cout << tot << endl;
for ( int i = 1 ; i <= tot ; i ++ )
{
cout << bcc[i].size() << ' ';
for ( auto v : bcc[i] )
cout << v << ' ' ;
cout << endl;
}
return 0;
}
P2860 [USACO06JAN] Redundant Paths G
缩点之后 统计一下入度大小
如果叶子节点
如果是奇数 那么是
#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
#define inl inline
#define eb emplace_back
#define endl '\n'
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define print(x) cerr<<#x<<'='<<x<<endl
#define getchar() cin.get()
const int N = 1e5 + 5;
int read()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , m , cnt = 1 , u[N] , v[N] , ans , dag[N];
int dfn[N] , low[N] , timer;
int sta[N] , top;
int id[N] , tot;
vector<pii> e[N];
inl void add ( int u , int v , int w ) { e[u].eb(v,w); }
void tarjan ( int u , int ff )
{
low[u] = dfn[u] = ++timer;
sta[++top] = u;
for ( auto [v,k] : e[u] )
{
if ( !dfn[v] ) tarjan ( v , k ) , low[u] = min ( low[u] , low[v] );
else if ( k ^ ( ff ^ 1 ) ) low[u] = min ( low[u] , dfn[v] );
}
if ( low[u] == dfn[u] )
{
++ tot;
while ( top )
{
int x = sta[top--];
id[x] = tot;
if ( x == u ) break;
}
}
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read();
for ( int i = 1 ; i <= m ; i ++ ) u[i] = read() , v[i] = read() , add ( u[i] , v[i] , ++ cnt ) , add ( v[i] , u[i] , ++ cnt );
for ( int i = 1 ; i <= n ; i ++ ) if ( !dfn[i] ) tarjan ( i , 0 );
for ( int i = 1 ; i <= m ; i ++ ) if ( id[u[i]] != id[v[i]] ) ++ dag[id[u[i]]] , ++ dag[id[v[i]]];
for ( int i = 1 ; i <= tot ; i ++ ) ans += ( dag[i] == 1 );
return cout << ans / 2 + ( ans & 1 ) << endl , 0;
}
圆方树
广义圆方树即为:在无向图中将所有点双缩起来 作为一个方点放在树中 圆点就是树上原有的节点
P4320 道路相遇
题意显然可以转化为
那么建立圆方树并树上差分统计即可 统计的是圆点的数量
#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
#define inl inline
#define eb emplace_back
#define endl '\n'
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define print(x) cerr<<#x<<'='<<x<<endl
#define getchar() cin.get()
const int N = 1e6 + 5;
int read()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , m , q , cnt , block , w[N] , sum[N];
int dfn[N] , low[N] , timer;
int sta[N] , tp;
vector<int> e[N] , ee[N];
inl void add ( int u , int v ) { e[u].eb(v); }
inl void adde ( int u , int v ) { ee[u].eb(v); }
void tarjan ( int u )
{
dfn[u] = low[u] = ++timer;
sta[++tp] = u;
for ( auto v : e[u] )
{
if ( !dfn[v] )
{
tarjan ( v ) , low[u] = min ( low[u] , low[v] );
if ( low[v] >= dfn[u] )
{
++ block;
while ( tp )
{
int x = sta[tp--];
adde ( block , x ) , adde ( x , block ) , w[x] = 1;
if ( x == v ) break;
}
adde ( block , u ) , adde ( u , block ) , w[u] = 1;
}
}
else low[u] = min ( low[u] , dfn[v] );
}
}
int fa[N] , dep[N] , sz[N] , son[N];
int top[N] , rev[N] , pos[N];
struct LCA
{
void dfs1 ( int u , int ff )
{
sum[u] = sum[fa[u]=ff] + w[u] , dep[u] = dep[ff] + 1 , sz[u] = 1;
for ( auto v : ee[u] )
if ( v ^ ff )
{
dfs1 ( v , u );
sz[u] += sz[v];
if ( sz[son[u]] < sz[v] ) son[u] = v;
}
}
void dfs2 ( int u , int tp )
{
top[u] = tp , pos[u] = ++timer , rev[timer] = u;
if ( son[u] ) dfs2 ( son[u] , tp );
for ( auto v : ee[u] ) if ( v ^ fa[u] && v ^ son[u] ) dfs2 ( v , v );
}
int lca ( int u , int v )
{
while ( top[u] != top[v] )
{
if ( dep[top[u]] < dep[top[v]] ) swap ( u , v );
u = fa[top[u]];
}
if ( dep[u] < dep[v] ) swap ( u , v );
return v;
}
}L;
int query ( int u , int v )
{
int lcaa = L.lca(u,v);
return sum[u] + sum[v] - sum[lcaa] - sum[fa[lcaa]];
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read() , block = n;
for ( int i = 1 , u , v ; i <= m ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
for ( int i = 1 ; i <= n ; i ++ ) if ( !dfn[i] ) tarjan ( i );
timer = 0;
L.dfs1 ( 1 , 0 ) , L.dfs2 ( 1 , 1 );
q = read();
for ( int i = 1 ; i <= q ; i ++ )
{
int u = read() , v = read();
cout << query ( u , v ) << endl;
}
return 0;
}
P4606 [SDOI2018] 战略游戏
显然我们求的是 一个集合中任意两点的路径并的子图中的白点数量除
如果直接求点的并的话不好统计
那么我们将节点的权值放到它和它父亲连的边上 那么所有路径上不重复白点的个数就是每两个
但是这样
注意 我们在新树中排序的时候 注意要用新树的
#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
#define inl inline
#define eb emplace_back
#define endl '\n'
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define print(x) cerr<<#x<<'='<<x<<endl
#define getchar() cin.get()
const int N = 1e6 + 5;
int read()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , m , q , cnt , block , w[N] , ans , a[N];
int dfn[N] , low[N] , timer;
int sta[N] , tp;
vector<int> e[N] , ee[N];
inl void add ( int u , int v ) { e[u].eb(v); }
inl void adde ( int u , int v ) { ee[u].eb(v); }
void tarjan ( int u )
{
dfn[u] = low[u] = ++timer;
sta[++tp] = u;
for ( auto v : e[u] )
if ( !dfn[v] )
{
tarjan(v) , low[u] = min ( low[u] , low[v] );
if ( low[v] >= dfn[u] )
{
++ block;
while ( tp )
{
int x = sta[tp--];
adde ( block , x ) , adde ( x , block ) , w[x] = 1;
if ( x == v ) break;
}
adde ( block , u ) , adde ( u , block ) , w[u] = 1;
}
}
else low[u] = min ( low[u] , dfn[v] );
}
int sz[N] , fa[N] , dep[N] , sum[N] , son[N];
int tim , rev[N] , pos[N] , top[N];
struct node
{
void dfs1 ( int u , int ff )
{
fa[u] = ff , sum[u] = sum[ff] + w[u] , dep[u] = dep[ff] + 1 , sz[u] = 1;
for ( auto v : ee[u] )
if ( v ^ ff )
{
dfs1 ( v , u );
sz[u] += sz[v];
if ( sz[son[u]] < sz[v] ) son[u] = v;
}
}
void dfs2 ( int u , int tp )
{
top[u] = tp , pos[u] = ++ tim , rev[tim] = u;
if ( son[u] ) dfs2 ( son[u] , tp );
for ( auto v : ee[u] ) if ( v ^ fa[u] && v ^ son[u] ) dfs2 ( v , v );
}
int lca ( int u , int v )
{
while ( top[u] != top[v] )
{
if ( dep[top[u]] < dep[top[v]] ) swap ( u , v );
u = fa[top[u]];
}
if ( dep[u] < dep[v] ) swap ( u , v );
return v;
}
}L;
void init()
{
for ( int i = 1 ; i <= 2 * n ; i ++ ) e[i].clear();
for ( int i = 1 ; i <= 2 * n ; i ++ ) ee[i].clear();
memset ( son , 0 , sizeof son );
memset ( w , 0 , sizeof w );
memset ( dfn , 0 , sizeof dfn );
memset ( sum , 0 , sizeof sum );
tim = timer = tp = ans = 0;
}
int dis ( int u , int v )
{
int lcaa = L.lca ( u , v );
return sum[u] + sum[v] - 2 * sum[lcaa];
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
int T = read();
while ( T -- )
{
init();
n = read() , m = read() , block = n;
for ( int i = 1 , u , v ; i <= m ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
for ( int i = 1 ; i <= n ; i ++ ) if ( !dfn[i] ) tarjan(i);
L.dfs1 ( 1 , 0 ) , L.dfs2 ( 1 , 1 );
q = read();
for ( int i = 1 ; i <= q ; i ++ )
{
ans = 0;
int s = read();
for ( int j = 1 ; j <= s ; j ++ ) a[j] = read();
sort ( a + 1 , a + s + 1 , [](const int &a , const int &b) { return pos[a] < pos[b]; } );
a[s+1] = a[1];
for ( int j = 1 ; j <= s ; j ++ ) ans += dis ( a[j] , a[j+1] );
ans >>= 1 , ans -= s;
ans += w[L.lca(a[1],a[s])];
cout << ans << endl;
}
}
return 0;
}
P3854 [TJOI2008] 通讯网破坏
相当于在圆方树上询问是否第三个点是否在第一个点和第二个点的路径上 类比仓鼠那道题即可得解
#include<bits/stdc++.h>
using namespace std;
#define mid ((l+r)>>1)
#define inl inline
#define eb emplace_back
#define endl '\n'
#define ls p<<1
#define rs p<<1|1
#define lson ls,l,mid
#define rson rs,mid+1,r
#define pii pair<int,int>
#define fi first
#define se second
#define mkp make_pair
#define print(x) cerr<<#x<<'='<<x<<endl
#define getchar() cin.get()
const int N = 1e6 + 5;
int read()
{
int x = 0 , f = 1;
char ch = getchar();
while ( !isdigit(ch) ) { if ( ch == '-' ) f = -1; ch = getchar(); }
while ( isdigit(ch) ) { x = ( x << 1 ) + ( x << 3 ) + ( ch ^ 48 ); ch = getchar(); }
return x * f;
}
int n , m , q , cnt , block , w[N] , ans , a[N];
int dfn[N] , low[N] , timer;
int sta[N] , tp;
vector<int> e[N] , ee[N];
inl void add ( int u , int v ) { e[u].eb(v); }
inl void adde ( int u , int v ) { ee[u].eb(v); }
void tarjan ( int u )
{
dfn[u] = low[u] = ++timer;
sta[++tp] = u;
for ( auto v : e[u] )
if ( !dfn[v] )
{
tarjan(v) , low[u] = min ( low[u] , low[v] );
if ( low[v] >= dfn[u] )
{
++ block;
while ( tp )
{
int x = sta[tp--];
adde ( block , x ) , adde ( x , block ) , w[x] = 1;
if ( x == v ) break;
}
adde ( block , u ) , adde ( u , block ) , w[u] = 1;
}
}
else low[u] = min ( low[u] , dfn[v] );
}
int sz[N] , fa[N] , dep[N] , sum[N] , son[N];
int tim , rev[N] , pos[N] , top[N];
struct node
{
void dfs1 ( int u , int ff )
{
fa[u] = ff , sum[u] = sum[ff] + w[u] , dep[u] = dep[ff] + 1 , sz[u] = 1;
for ( auto v : ee[u] )
if ( v ^ ff )
{
dfs1 ( v , u );
sz[u] += sz[v];
if ( sz[son[u]] < sz[v] ) son[u] = v;
}
}
void dfs2 ( int u , int tp )
{
top[u] = tp , pos[u] = ++ tim , rev[tim] = u;
if ( son[u] ) dfs2 ( son[u] , tp );
for ( auto v : ee[u] ) if ( v ^ fa[u] && v ^ son[u] ) dfs2 ( v , v );
}
int lca ( int u , int v )
{
while ( top[u] != top[v] )
{
if ( dep[top[u]] < dep[top[v]] ) swap ( u , v );
u = fa[top[u]];
}
if ( dep[u] < dep[v] ) swap ( u , v );
return v;
}
}L;
int dis ( int u , int v )
{
int lcaa = L.lca ( u , v );
return dep[u] + dep[v] - 2 * dep[lcaa];
}
signed main ()
{
ios::sync_with_stdio(false);
cin.tie(0) , cout.tie(0);
n = read() , m = read() , block = n;
for ( int i = 1 , u , v ; i <= m ; i ++ ) u = read() , v = read() , add ( u , v ) , add ( v , u );
for ( int i = 1 ; i <= n ; i ++ ) if ( !dfn[i] ) tarjan(i);
L.dfs1 ( 1 , 0 ) , L.dfs2 ( 1 , 1 );
q = read();
for ( int i = 1 ; i <= q ; i ++ )
{
int s = read() , t = read() , c = read();
if ( dis ( s , t ) == dis ( s , c ) + dis ( c , t ) ) puts("yes");
else puts("no");
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】