CF97E Leaders

考虑跑出 dfs 树。如果两个点在 dfs 树上路径本身长度就是奇数,可以直接走过去。

否则,相当于可以在这两点之间的路径上有两个点 $u,v$ ,可以通过在 $u$ 离开 dfs 树上的路径然后从 $v$ 回来继续走到另一个点的方式改变奇偶性。不难发现,这个条件等价于 $u,v$ 之间的路径存在于一个奇环上。同时我们还有一个结论,一个点双(因为这题要求点不相交)内部如果有一个奇环,那么点双内部任何两个点都处在一个奇环。

于是我们考虑给所有点双内有奇环的点打个标记,然后树上差分,询问的时候就询问一下两个点之间的路径上是否有点处于点双即可。

但是还是会出现一些会导致这个东西不太对的情况。

4YW71N@8_JFEUOTY_3__4JX.png

也就是说,进入一个点双了之后没办法通过不经过重复点跑出来。

我们可以把进入一个点双的点不标记为在这个点双之内。这样操作后 dfs 树路径上的那个点一定不会被计算贡献。因为这是 dfs 树,做 tarjan 的时候必然先会跑 dfs 树边,可以看出下面那个点双一定是在跑这个点访问到的。

于是在上面那种情况,路径上的点不会造成贡献。但是如果一个奇环能作为让长度变成奇数的,也就是在路径上既可以进有可以出,那么在这个路径上肯定存在至少两个点,在这个点双内部,所以肯定是有贡献的。

同时还得除去 LCA 的贡献:

image.png

考虑这种情况,LCA 确实不是访问到所在点双的点,但是一样不能通过上面那个路径改变奇偶。这种情况很好解决,不计算 LCA 的贡献即可。如果一个与 LCA 有关的奇环可以被用,那么可以发现一定也还有一个点在路径上。

(好像还有不需要 tarjan 求点双直接整奇环的做法,但细节感觉可能比这个还多)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "cmath"
#include "vector"
#include "map"
#include "set"
#include "queue"
using namespace std;
#define MAXN 1000006
//#define int long long
#define rep(i, a, b) for (int i = (a), i##end = (b); i <= i##end; ++i)
#define per(i, a, b) for (int i = (a), i##end = (b); i >= i##end; --i)
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define vi vector<int>
#define all(x) (x).begin() , (x).end()
#define mem( a ) memset( a , 0 , sizeof a )
typedef long long ll;
int n , m , q;
vi G[MAXN];

int dfn[MAXN] , low[MAXN] , g[MAXN][19] , clo , stk[MAXN] , top , ins[MAXN] , dep[MAXN] , odd[MAXN];
int od[MAXN] , bel[MAXN];
void tarjan( int u , int f ) {
bel[u] = bel[f];
dfn[u] = low[u] = ++ clo , ins[u] = 1;
stk[++ top] = u;
for( int v : G[u] ) {
if( v == f ) continue;
if( !dfn[v] ) {
g[v][0] = u;
rep( k , 1 , 18 ) if( g[g[v][k-1]][k-1] ) g[v][k] = g[g[v][k-1]][k-1]; else break;
dep[v] = dep[u] + 1;
tarjan( v , u ) , low[u] = min( low[u] , low[v] );
}
else if( ins[v] ) low[u] = min( low[u] , dfn[v] ) , odd[u] |= ( ~( dep[v] + dep[u] ) & 1 );
}
if( dfn[u] == low[u] ) {
int flg = 0 , t = top;
while( stk[t] != u && !flg ) flg = odd[stk[t]] , -- t;
while( stk[top] != u ) {
od[stk[top]] |= flg;
ins[stk[top]] = 0;
-- top;
}
-- top , ins[u] = 0;
}
}

inline int lca( int u , int v ) {
if( dep[u] < dep[v] ) u ^= v ^= u ^= v;
if( dep[u] != dep[v] ) per( k , 18 , 0 ) if( dep[g[u][k]] >= dep[v] ) u = g[u][k];
if( u == v ) return u;
per( k , 18 , 0 ) if( g[u][k] != g[v][k] ) u = g[u][k] , v = g[v][k];
return g[u][0];
}

int S[MAXN];
void dfs( int u , int f ) {
S[u] += od[u];
for( int v : G[u] ) if( v != f && dep[v] == dep[u] + 1 ) S[v] += S[u] , dfs( v , u );
}

inline bool fuck( int u , int v ) {
if( bel[u] != bel[v] || u == v ) return false;
if( dep[u] + dep[v] & 1 ) return true;
return S[u] + S[v] - 2 * S[lca( u , v )] > 0;
}

void solve() {
cin >> n >> m;
int u , v;
rep( i , 1 , m ) {
scanf("%d%d",&u,&v);
G[u].pb( v ) , G[v].pb( u );
}
rep( i , 1 , n ) if( !dfn[i] ) bel[i] = i , dep[i] = 1 , tarjan( i , i ) , dfs( i , i );
cin >> q;
rep( i , 1 , q ) scanf("%d%d",&u,&v) , puts( fuck( u , v ) ? "Yes" : "No" );
}

signed main() {
// int T;cin >> T;while( T-- ) solve();
solve();
}
文章作者: yijan
文章链接: https://yijan.co/cf97e-leaders/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 Yijan's Blog