zoukankan      html  css  js  c++  java
  • CodeForces 97 E. Leaders(点双连通分量 + 倍增)

    题意

    给你一个有 (n) 个点 (m) 条边的无向图,有 (q) 次询问,每次询问两个点 (u, v) 之间是否存在长度为奇数的简单路径。

    (1 le n, m, q le 10^5)

    题解

    显然我们可以对于每个联通块单独处理,如果 (u, v) 不联通显然就不存在这条路。

    然后对于每个联通块,首先随便弄一颗生成树。

    1. 如果这 (u o v) 在树上的路径长就为奇数,显然是可以的,这个可以预处理深度就行了。
    2. 否则,(u o v) 在树上的路径的边,只要存在一条边属于一个奇环就行了。

    然后我们只要求出每条边是否属于一个奇环就行了。

    单是求环的话,我们显然可以用求点双联通分量的方法来求的,求奇环需要用到下面的一个性质。

    一个点双连通分量中要么每条边都在至少一个奇环上, 要么没有奇环.

    这个证明是很显然的,可以自己手动画图理解。这个性质可以记住,到时候或许有用。

    所以如果点双中存在一条边属于奇环,那么所有边都是在奇环上。

    判断奇环可以直接判这个点连的两条边奇偶性是否相同,如果相同那么就是奇环了。


    然后我们需要查询两个点的路径上是否存在一条边属于奇环,这个可以直接用树上差分做。

    然后对于之前记一条边是否在奇环上,需要把这个点双中除了点双中最上面的那个点 (u) 全部标成 (1)

    也就是我们都标到儿子上就行了。

    实现起来就是 (cnt[u] + cnt[v] - 2 * cnt[Get_Lca(u, v)]) > 0cnt 为到根路径上标号的前缀和,用倍增求 Lca 就行了。

    总结

    多找性质,多思考,多记性质。

    代码

    具体实现看代码就行啦qwq 注意此处点双要存边才行,还要注意一些小细节。

    #include <bits/stdc++.h>
    
    #define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
    #define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
    #define Set(a, v) memset(a, v, sizeof(a))
    #define Cpy(a, b) memcpy(a, b, sizeof(a))
    #define debug(x) cout << #x << ": " << x << endl
    #define DEBUG(...) fprintf(stderr, __VA_ARGS__)
    #define fir first
    #define sec second
    #define mp make_pair
    
    using namespace std;
    
    typedef pair<int, int> PII;
    
    inline bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
    inline bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
    
    inline int read() {
        int x = 0, fh = 1; char ch = getchar();
        for (; !isdigit(ch); ch = getchar()) if (ch == '-') fh = -1;
        for (; isdigit(ch); ch = getchar()) x = (x << 1) + (x << 3) + (ch ^ 48);
        return x * fh;
    }
    
    void File() {
    #ifdef zjp_shadow
    	freopen ("E.in", "r", stdin);
    	freopen ("E.out", "w", stdout);
    #endif
    }
    
    const int N = 1e5 + 1e3, M = N << 1;
    
    int n, m, Logn;
    
    int anc[N][20], dep[N];
    vector<int> G[N]; bitset<N> vis1, vis2; int id[N], version;
    void Dfs_Init(int u, int fa = 0) {
    	id[u] = version; vis1[u] = true; dep[u] = dep[anc[u][0] = fa] + 1;
    	For (i, 1, Logn) anc[u][i] = anc[anc[u][i - 1]][i - 1];
    	for (int v : G[u]) if (!vis1[v]) Dfs_Init(v, u);
    }
    
    int dfn[N], lowlink[N], cnt[N];
    PII sta[M]; int top = 0;
    void Tarjan(int u, int fa = 0) {
    	static int clk = 0;
    	dfn[u] = lowlink[u] = ++ clk;
    
    	for (int v : G[u]) if (v != fa && dfn[v] < dfn[u]) {
    		sta[++ top] = mp(u, v);
    		if (!dfn[v]) {
    			Tarjan(v, u);
    			chkmin(lowlink[u], lowlink[v]);
    			if (lowlink[v] >= dfn[u]) {
    				int tmp = top, x, y, flag = 0;
    				do {
    					x = sta[top].fir; y = sta[top --].sec;
    					if ((dep[x] & 1) == (dep[y] & 1)) { flag = 1; break; }
    				} while (!(x == u && y == v));
    				if (!flag) continue ;
    				top = tmp;
    				do {
    					x = sta[top].fir; y = sta[top --].sec;
    					cnt[x] = cnt[y] = 1;
    				} while (!(x == u && y == v));
    				cnt[u] = 0;
    			}
    		} else chkmin(lowlink[u], dfn[v]);
    	}
    }
    
    void Dfs_Calc(int u, int fa = 0) {
    	cnt[u] += cnt[fa]; vis2[u] = true;
    	for (int v : G[u]) if (!vis2[v]) Dfs_Calc(v, u);
    }
    
    inline int Get_Lca(int x, int y) {
    	if (dep[x] < dep[y]) swap(x, y);
    	Fordown (i, Logn, 0)
    		if (dep[anc[x][i]] >= dep[y]) x = anc[x][i];
    	if (x == y) return x;
    	Fordown (i, Logn, 0)
    		if (anc[x][i] != anc[y][i]) x = anc[x][i], y = anc[y][i];
    	return anc[x][0];
    }
    
    inline bool Check(int u, int v) {
    	if (id[u] != id[v]) return false;
    	if ((dep[u] & 1) ^ (dep[v] & 1)) return true;
    	return (cnt[u] + cnt[v] - 2 * cnt[Get_Lca(u, v)]) > 0;
    }
    
    int main () {
    
    	File();
    
    	n = read(); m = read();
    	Logn = ceil(log2(n));
    
    	For (i, 1, m) {
    		int u = read(), v = read();
    		G[u].push_back(v); G[v].push_back(u);
    	}
    	For (i, 1, n) if (!dfn[i]) ++ version, Dfs_Init(i), Tarjan(i), Dfs_Calc(i);
    
    	int q = read();
    	For (i, 1, q) {
    		int u = read(), v = read();
    		puts(Check(u, v) ? "Yes" : "No");
    	}
    
    	return 0;
    }
    
  • 相关阅读:
    24. Swap Nodes in Pairs
    23. Merge k Sorted Lists
    shell脚本报错:"[: =: unary operator expected"
    一种用 数组元素 指定 所调函数 的方法
    阻塞 非阻塞
    Linux open() 一个函数,两个函数原型
    QT 执行windows cmd 命令并读取结果
    Qt5 escape spaces in path
    获取磁盘的 总容量,空余容量,已用容量 【windows】
    通过进程名称,获取其路径
  • 原文地址:https://www.cnblogs.com/zjp-shadow/p/9550817.html
Copyright © 2011-2022 走看看