Official

F - Good Set Query Editorial by en_translator


In order to solve this problem, during the simulation of the operations in the problem, we want to perform the following two kinds of operations while scanning the triplets \((a_i, b_i, d_i)\) for each \(i = 1, 2, \ldots, Q\):

  • Operation A: determine if the constraints imposed so far uniquely determine \(X_{a_i}- X_{b_i}\), and find it if they do.
  • Operation B: impose new constraint \(X_{a_i} - X_{b_i} = d_i\)

We now consider how to perform operations A and B fast enough. Here, we do not impose a new constraint if it causes an inconsistency (if no sequence \(X\) satisfies all the constraints when it is imposed). To come to the point, they can be achieved by a data structure called weighted DSU (Disjoint Set Union).

First, we introduce the following operation A’, which is a simplified version of operation B, to consider how to process operations A’ and B fast enough:

  • Operation A’: determines if the constraints imposed so far uniquely determines \(X_{a_i}- X_{b_i}\) (without finding its specific value).

This can be done with an ordinary DSU. Specifically, manage \(N\) vertices \(1, 2, \ldots, N\) corresponding to the \(N\) elements \(X_1, X_2, \ldots, X_N\) of the sequence \(X\) in a DSU; then one can connect vertices \(a_i\) and \(b_i\) to process operation A’, and determine if vertices \(a_i\) and \(b_i\) belong to the same connected component to process operation B.

To support operation A, one can additionally maintain a length-\(N\) sequence \(W = (W_1, W_2, \ldots, W_N)\) defined below, as well as an ordinary DSU:

\(W_v \coloneqq \) (the unique value \(X_p - X_v\), where \(p\) is the parent vertex of vertex \(v\) in the DSU),

or \(W_v \coloneqq 0\) if \(v\) does not have a parent.

For example, if an operation A makes vertices \(a_i\) and \(b_i\) belong to the same connected component, one can find the unique value \(X_{a_i} - X_{b_i}\) by following the path from vertices \(a_i\) and \(b_i\) to the root \(r\) of the directed tree that they belong, namely paths \(a_i = u_1 \rightarrow u_2 \rightarrow \cdots \rightarrow u_x = r\) and \(b_i = v_1 \rightarrow v_2 \rightarrow \cdots \rightarrow v_y = r\), so as to evaluate

\[ (W_{v_1} + W_{v_2} + \cdots + W_{v_{y-1}}) - (W_{u_1} + W_{u_2} + \cdots + W_{u_{x-1}}). \]

(The length of the paths to follow are short enough if an appropriate technique like union by rank or path compression is performed when managing the DSU, so it can be evaluated fast enough.)

A weighted DSU is a data structure such that information like \(W\) above as well as that of ordinary DSU are maintained, and \(W\) is updated on modifying the DSU itself.

The following is sample code for this problem in C++ language.

#include <iostream>
typedef long long ll;
using namespace std;

ll n, Q;
ll parent[200001], weight[200001];

int root(int i){
	if(parent[i] == i) return i;
	int r = root(parent[i]);
	weight[i] = weight[parent[i]] + weight[i];
	return parent[i] = r;
}
bool unite(int i, int j, ll x){
	int root_i = root(i), root_j = root(j);
	if(root_i == root_j) return weight[j]-weight[i] == x;
	parent[root_i] = root_j;
	weight[root_i] = weight[j]-weight[i]-x;
	return true;
}

int main(void)
{
	cin >> n >> Q;
	for(int i = 1; i <= n; i++) parent[i] = i, weight[i] = 0;
	
	ll a, b, d;
	for(int i = 1; i <= Q; i++){
		cin >> a >> b >> d;
		if(unite(a, b, d)) cout << i << " ";
	}
	cout << endl;

	return 0;
}

posted:
last update: