典中典 LCT。

题目保证了 C≤10C\le10,而又保证了图有如下性质:

  1. 对于任意节点连出去的边中,相同颜色的边不超过两条。

  2. 图中不存在同色的环,同色的环指相同颜色的边构成的环。

这事实上保证了每种颜色的图单独拎出来是一个森林。

考虑对于每种颜色维护一颗 LCT。

修改点权是平凡的,在每一颗上都直接修改即可;

查询操作直接在对应颜色的 LCT 查即可;

修改边颜色相对复杂。拿一个数组统计对应颜色的边的数量。一个 map 维护边的存在性。对于能修改的情况,就断掉原颜色上 LCT 的连接,建立新连接。注意更新那个统计数组和 map。细节看代码吧。

::::success[Code]

#include<bits/stdc++.h>
using namespace std;
const int N = 1e4+5;
struct LCT {
	int ch[N][2], fa[N], val[N], maxn[N], rev[N], stk[N];
	inline int get(int x) {
		return ch[fa[x]][1] == x;
	}
	inline bool is_root(int x) {
		return ch[fa[x]][0] != x && ch[fa[x]][1] != x;
	}
	inline void pushup(int x) {
		maxn[x] = val[x];
		if (ch[x][0])
			maxn[x] = max(maxn[x], maxn[ch[x][0]]);
		if (ch[x][1])
			maxn[x] = max(maxn[x], maxn[ch[x][1]]);
	}
	inline void pushdown(int x) {
		if (rev[x]) {
			swap(ch[x][0], ch[x][1]);
			if (ch[x][0])
				rev[ch[x][0]] ^= 1;
			if (ch[x][1])
				rev[ch[x][1]] ^= 1;
			rev[x] = 0;
		}
	}
	inline void rotate(int x) {
		int y = fa[x], z = fa[y];
		int k = get(x);
		if (!is_root(y))
			ch[z][get(y)] = x;
		fa[x] = z;
		ch[y][k] = ch[x][k ^ 1];
		if (ch[x][k ^ 1])
			fa[ch[x][k ^ 1]] = y;
		ch[x][k ^ 1] = y;
		fa[y] = x;
		pushup(y);
		pushup(x);
	}
	void splay(int x) {
		int top = 0, cur = x;
		stk[++top] = cur;
		while (!is_root(cur))
			cur = fa[cur], stk[++top] = cur;
		while (top)
			pushdown(stk[top--]);
		while (!is_root(x)) {
			int y = fa[x];
			if (!is_root(y)) {
				if (get(x) == get(y))
					rotate(y);
				else
					rotate(x);
			}
			rotate(x);
		}
	}
	void access(int x) {
		for (int y = 0; x; y = x, x = fa[x]) {
			splay(x);
			ch[x][1] = y;
			pushup(x);
		}
	}
	void make_root(int x) {
		access(x);
		splay(x);
		rev[x] ^= 1;
	}
	int find_root(int x) {
		access(x);
		splay(x);
		pushdown(x);
		while (ch[x][0])
			x = ch[x][0], pushdown(x);
		splay(x);
		return x;
	}
	void link(int x, int y) {
		make_root(x);
		if (find_root(y) != x)
			fa[x] = y;
	}
	void cut(int x, int y) {
		make_root(x);
		if (find_root(y) == x && fa[y] == x && !ch[y][0])
			fa[y] = ch[x][1] = 0, pushup(x);
	}
	void update(int x, int k) {
		splay(x);
		val[x] = k;
		pushup(x);
	}
	int query(int x, int y) {
		make_root(x);
		if (find_root(y) != x)
			return -1;
		return maxn[x];
	}
} lct[11];
int cnt[11][N];
map<pair<int, int>, int> mp;
int main() {
	cin.tie(nullptr)->sync_with_stdio(false);
	int n, m, C, k;
	cin >> n >> m >> C >> k;
	for (int i = 1; i <= n; i++) {
		int w;
		cin >> w;
		for (int c = 0; c < C; c++)
			lct[c].val[i] = lct[c].maxn[i] = w;
	}
	for (int i = 1; i <= m; i++) {
		int u, v, c;
		cin >> u >> v >> c;
		if (u > v)
			swap(u, v);
		mp[ {u, v}] = c;
		lct[c].link(u, v);
		cnt[c][u]++;
		cnt[c][v]++;
	}
	while (k--) {
		int op;
		cin >> op;
		if (op == 0) {
			int x, y;
			cin >> x >> y;
			for (int c = 0; c < C; c++) lct[c].update(x, y);
		} else if (op == 1) {
			int u, v, w;
			cin >> u >> v >> w;
			if (u > v)
				swap(u, v);
			if (mp.find({u, v}) == mp.end()) {
				cout << "No such edge.\n";
			} else {
				int o = mp[ {u, v}];
				if (o == w) {
					cout << "Success.\n";
				} else if (cnt[w][u] > 1 || cnt[w][v] > 1) {
					cout << "Error 1.\n";
				} else if (lct[w].find_root(u) == lct[w].find_root(v)) {
					cout << "Error 2.\n";
				} else {
					lct[o].cut(u, v);
					cnt[o][u]--;
					cnt[o][v]--;
					lct[w].link(u, v);
					cnt[w][u]++;
					cnt[w][v]++;
					mp[ {u, v}] = w;
					cout << "Success.\n";
				}
			}
		} else {
			int c, u, v;
			cin >> c >> u >> v;
			cout << lct[c].query(u, v) << '\n';
		}
	}
	return 0;
}

::::