传送门
中文题面就不在阐述
解题思路:题目上的操作2 对一棵树的一条链进行操作,很容易想到树链剖分。由操作1给的叙述,我们能够想到并查集离线建树。要求一个链上知识点种类的数量,而且 wi 不超过60,利用状压来解决。
///#include<bits/stdc++.h>
///#include<unordered_maps>
///#include<unordered_set>
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<cmath>
#include<queue>
#include<bitset>
#include<set>
#include<stack>
#include<map>
#include<list>
#include<new>
#include<vector>
#define MT(a, b) memset(a,b,sizeof(a));
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const double pai = acos(-1.0);
const double E = 2.718281828459;
const ll mod = 1000000007;
const int INF = 0x3f3f3f3f;
const int maxn = 1e5 + 5;
int n, q;
ll value[maxn];
struct node {
int e;
int p;
} load[maxn << 1];
int head[maxn], sign;
void add_edge(int s, int e) {
load[++sign] = node{e, head[s]};
head[s] = sign;
}
struct node_op {
int op;
int x;
int y;
} op[maxn * 3];
int father[maxn], sizes[maxn], depth[maxn], h_son[maxn];
int top[maxn], id[maxn], ranks[maxn], times;
void dfs1(int s, int pre) {
sizes[s] = 1;
depth[s] = depth[pre] + 1;
for (int i = head[s], e; ~i; i = load[i].p) {
e = load[i].e;
if (e ^ pre) {
father[e] = s;
dfs1(e, s);
sizes[s] += sizes[e];
if (sizes[e] > sizes[h_son[s]])
h_son[s] = e;
}
}
}
void dfs2(int s, int root) {
top[s] = root;
id[s] = ++times;
ranks[times] = s;
if (!h_son[s])
return;
dfs2(h_son[s], root);
for (int i = head[s], e; ~i; i = load[i].p) {
e = load[i].e;
if (e != father[s] && e != h_son[s])
dfs2(e, e);
}
}
int p[maxn];
int find(int x) {
return p[x] == x ? x : p[x] = find(p[x]);
}
ll state[maxn << 2]; ///状压数组
void pushup(int sign) {
state[sign] = state[sign << 1] | state[sign << 1 | 1];
}
void build(int sign, int l, int r) {
if (l == r) {
state[sign] = (1 << value[ranks[sign]]);
return;
}
int mid = (l + r) >> 1;
build(sign << 1, l, mid);
build(sign << 1 | 1, mid + 1, r);
pushup(sign);
}
void update(int sign, int l, int r, int s, ll x) {
if (l == r) {
state[sign] = 1LL << x; ///注意要用 1LL
return;
}
int mid = (l + r) >> 1;
if (s <= mid) update(sign << 1, l, mid, s, x);
else update(sign << 1 | 1, mid + 1, r, s, x);
pushup(sign);
}
ll query(int sign, int l, int r, int a, int b) {
if (l == a && r == b) return state[sign];
int mid = (l + r) >> 1;
if (b <= mid) return query(sign << 1, l, mid, a, b);
else if (a > mid) return query(sign << 1 | 1, mid + 1, r, a, b);
else return query(sign << 1, l, mid, a, mid) | query(sign << 1 | 1, mid + 1, r, mid + 1, b);
}
ll query_line(int a, int b) {
ll ans = 0;
while (top[a] != top[b]) {
if (depth[top[a]] > depth[top[b]])
swap(a, b);
ans = ans | query(1, 1, n, id[top[b]], id[b]);
b = father[top[b]];
}
if (depth[a] > depth[b])
swap(a, b);
ans = ans | query(1, 1, n, id[a], id[b]);
return ans;
}
void init() {
sign = times = 0;
memset(state, 0, sizeof(state));
for (int i = 0; i < maxn; i++) {
h_son[i] = depth[i] = father[i] = sizes[i] = 0;
top[i] = id[i] = ranks[i] = 0;
head[i] = -1;
p[i] = i;
}
}
int main() {
init();
int s, e;
scanf("%d %d", &n, &q);
for (int i = 1; i <= n; i++) {
scanf("%lld", &value[i]);
}
for (int i = 1, a, b; i <= q; i++) {
scanf("%d %d %d", &op[i].op, &op[i].x, &op[i].y);
if (op[i].op == 1) {
a = find(op[i].x);
b = find(op[i].y);
if (a != b) {
p[a] = b;
add_edge(op[i].y, op[i].x);
add_edge(op[i].x, op[i].y);
}
}
}
dfs1(1, 0);
dfs2(1, 1);
build(1, 1, n);
for (int i = 0; i < maxn; i++) p[i] = i;
for (int i = 1, a, b; i <= q; i++) {
if (op[i].op == 1) {
a = find(op[i].x);
b = find(op[i].y);
if (a != b) p[a] = b;
else continue;
ll x = value[op[i].x];
ll y = value[op[i].y];
ll z = (x + y) / 2;
update(1, 1, n, id[op[i].x], z);
update(1, 1, n, id[op[i].y], z);
value[op[i].x] = value[op[i].y] = z;
} else {
if (find(op[i].y) != find(op[i].x))
printf("-1\n");
else {
ll ans = query_line(op[i].x, op[i].y), cnt = 0;
for (int i = 0; i <= 60; i++) {
if (ans >> i & 1) cnt++;
}
printf("%d\n", cnt);
}
}
}
}