CodeForces 1555F Good Graph

题目传送门:CodeForces 1555F Good Graph


Statement:

定义一张图为好图当且仅当图中所有简单环的\(xor\)\(1\)。按顺序给定\(q\)条边,边权\(\in\{0,1\}\)。初始图上无边,按顺序依次考虑每条边,若这条边加入后图依旧是好图,那么可以加入,否则不能加入。输出每条边是否加入。

\(n,q\leq 3\cdot10^{5}\)


Solution:

可以发现图中一定不存在两个环有公共边。

那么一开始把改变联通性的边先加入,这样显然不影响答案。

加完后图变成了森林,再依次考虑构成环的边。

\(u\)\(v\)的路径上已经存在一个环了,那么这条边不能加入,否则加入并且把\(u\)\(v\)路径上的打上标记。

边上打标记显然可以转成向点打标记。

这样看上去要树剖,但是认真分析后可以发现每条边只会被加人一次,也就是每个点只会被加人一次,那么可以用DFS序的树状数组维护子树加,查询链和。

时间复杂度为\(\mathcal O(Q\log_2N)\)

code

#include <bits/stdc++.h>

template <typename T>
struct BIT {
	int N;
	std::vector <T> Tree;
	
	BIT () {}
	BIT (int n) : N(n) {
		Tree = std::vector <int> (N + 1, 0);
	}
	
	inline void add(int p, int v) {
		while (p <= N)
			Tree[p] += v, p += p & -p;
	}
	
	inline int query(int p) {
		p = std::min(p, N);
		int res = 0;
		while (p)
			res += Tree[p], p -= p & -p;
		return res;
	}
};

int main(void) {
	std::ios::sync_with_stdio(false);
	std::cin.tie(nullptr);
	std::cout.tie(nullptr);
	
	int n, q;
	std::cin >> n >> q;
	
	std::vector <std::tuple <int, int, int>> A;
	std::vector <int> uf(n, -1);
	
	std::function <int(int)> findPa = [&](int a) {
		return uf[a] < 0 ? a : (uf[a] = findPa(uf[a]));
	};
	
	auto mergeTree = [&](int a, int b) {
		a = findPa(a), b = findPa(b);
		if (a ^ b) {
			if (uf[a] > uf[b])
				std::swap(a, b);
			uf[a] += uf[b], uf[b] = a;
			return true;
		}
		return false;
	};
	
	std::vector <std::vector <std::pair <int, int>>> adj(n);
	std::vector <std::vector <int>> query(n);
	std::vector <bool> isLink(q, 0);
	for (int i = 0; i < q; ++i) {
		int u, v, w;
		std::cin >> u >> v >> w, --u, --v;
		A.push_back(std::make_tuple(u, v, w));
		
		if (mergeTree(u, v)) {
			adj[u].push_back(std::make_pair(v, w)),
			adj[v].push_back(std::make_pair(u, w));
			isLink[i] = true;
		} else {
			query[u].push_back(i), query[v].push_back(i);
		}
	}
	
	std::vector <int> Lca(q, 0), dfn(n, 0), fa(n, 0), sz(n, 0), dist(n, 0);
	for (int i = 0; i < n; ++i)
		uf[i] = -1;
	
	int _clock = 0;
	std::function <void(int, int)> getLca = [&](int u, int pa) {
		dfn[u] = ++_clock;
		sz[u] = 1;
		
		for (auto nxt : adj[u]) {
			int v = nxt.first;
			int w = nxt.second;
			if (v == pa)
				continue;
			
			dist[v] = dist[u] ^ w, getLca(v, u), sz[u] += sz[v];
			uf[v] = fa[v] = u;
		}
		
		for (int i : query[u]) {
			int p = std::get <0> (A[i]);
			int q = std::get <1> (A[i]);
			int o = p ^ q ^ u;
			if (dfn[o])
				Lca[i] = findPa(o);
		}
	};
	
	for (int i = 0; i < n; ++i)
		if (!dfn[i])
			getLca(i, i);
	
	BIT <int> R(n + 1);
	for (int i = 0; i < q; ++i) {
		if (isLink[i]) {
			std::cout << "Yes\n";
			continue;
		}
		
		int u = std::get <0> (A[i]);
		int v = std::get <1> (A[i]);
		int w = std::get <2> (A[i]);
		int lca = Lca[i];
		
		if ((dist[u] ^ dist[v] ^ w) && R.query(dfn[u]) == R.query(dfn[lca]) && R.query(dfn[v]) == R.query(dfn[lca])) {
			std::cout << "Yes\n";
			while (u ^ lca) {
				R.add(dfn[u], 1);
				R.add(dfn[u] + sz[u], -1);
				u = fa[u];
			}
			
			while (v ^ lca) {
				R.add(dfn[v], 1),
				R.add(dfn[v] + sz[v], -1);
				v = fa[v];
			}
		} else {
			std::cout << "No\n";
		}
	}
	
	return 0;
}
posted @ 2021-08-19 09:20  Beginner2670  阅读(71)  评论(0)    收藏  举报