题意:
给一棵树,点上有点权,每次询问u,v,k,表示询问u-v路径上出现次数第k多的点权,如果次数一样则点权越小当做次数越多。
做法:
首先肯定要树上莫队。
然后我们考虑怎么求出现次数的第k小。
发现我们要求的其实是一个二元组(次数,点权)的第k小。
于是可以考虑在这个序列上分块。
预处理出这个二元组排完序后的编号,然后分成根号块。
记录一个bsum[i]表示这一块内出现过的点的数量,sum[i]表示这个位置出现的点的数量。
查询的时候从右往左扫每一个块,查到某一个块再在块内查询。
注意修改一个节点的存在情况时有一些细节要注意。
复杂度nsqrt(n).
易错点:
(只有我会犯的易错点请忽略)
就是离散化的时候下标更改过了,之后要用更改后的下标范围(代码中的num_tot),否则会re的很惨。
代码:
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cctype>
#include<cmath>
#include<vector>
#define pb push_back
#define debug puts("orz")
using namespace std;
typedef long long ll;
inline ll read() {
char ch = getchar(); ll x = 0; int op = 1;
for(; !isdigit(ch); ch = getchar()) if(ch == '-') op = -1;
for(; isdigit(ch); ch = getchar()) x = x*10+ch-'0';
return x*op;
}
inline void write(ll a) {
if(a < 0) putchar('-'), a = -a;
if(a >= 10) write(a/10); putchar('0'+a%10);
}
const int N = 50010;
const int inf = 1e9;
int n, m, num_tot, tot, bsz, cnt, clk;
int a[N], b[N], num[N], head[N], f[N][25], depth[N], sz[N], bel[N], pos[N], bl[N], br[N], sum[N], bsum[N], tong[N], ans[N];
vector<int> id[N];
struct edge {
int to, nxt;
edge() {}
edge(int x, int y) { to = x, nxt = y; }
}e[N<<1];
struct questions { int x, y, k, id; }q[N];
struct node { int a, b; node() {} node(int x, int y) { a = x, b = y; } }qu[N];
const bool cmp(const questions &x, const questions &y) {
return bel[x.x] < bel[y.x] || bel[x.x] == bel[y.x] && pos[x.y] < pos[y.y];
}
const bool cmpnode(const node &x, const node &y) { return x.b < y.b || x.b == y.b && x.a < y.a; }
inline void addedge(int x, int y) { e[++ cnt] = edge(y, head[x]); head[x] = cnt; }
inline void dfs(int u, int lst, int s) {
pos[u] = ++ clk;
if(sz[tot] == bsz) sz[bel[u] = ++ tot] = 1; else sz[bel[u] = tot] ++;
depth[u] = s; f[u][0] = lst;
for(int i = head[u]; i; i = e[i].nxt) if(e[i].to != lst) dfs(e[i].to, u, s+1);
}
inline int lca(int x, int y) {
if(depth[x] < depth[y]) swap(x, y);
int tmp = depth[x] - depth[y];
for(int i = 16; i >= 0; i --) if(tmp>>i&1) x = f[x][i];
if(x == y) return x;
for(int i = 16; i >= 0; i --)
if(f[x][i] != f[y][i]) x = f[x][i], y = f[y][i];
return f[x][0];
}
inline void work(int x) {//改变节点x的状态
int c = a[x], i;//c颜色
if(b[x]) {
//删掉当前节点的颜色
i = id[c][tong[c]];//tong[c]是当前c颜色有几个
sum[i] --; bsum[bel[i]] --; tong[c] --;
if(tong[c]) {
i = id[c][tong[c]];
sum[i] ++; bsum[bel[i]] ++;
}
b[x] = 0;
} else {
//插入一个颜色为c的节点
if(tong[c]) {
i = id[c][tong[c]];
sum[i] --; bsum[bel[i]] --;
}
tong[c] ++; i = id[c][tong[c]];
sum[i] ++; bsum[bel[i]] ++;
b[x] = 1;
}
}
inline void change(int x, int y) { while(x != y) { work(x); x = f[x][0]; } }
inline int qry(int k) {
for(int i = tot; i >= 1; i --) if(bsum[i] >= k) {
for(int j = br[i]; j >= bl[i]; j --) if(sum[j]) {
if(k == 1) return num[qu[j].a];
k -= sum[j];
}
} else k -= bsum[i];
}
int main() {
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
n = read(), m = read();
for(int i = 1; i <= n; i ++) a[i] = num[i] = read();
sort(num+1, num+1+n); num_tot = unique(num+1, num+1+n)-num-1;
for(int i = 1; i <= n; i ++) {
a[i] = lower_bound(num+1, num+1+num_tot, a[i])-num;//这里注意不要写成num+1+n!
tong[a[i]] ++;
} int tail = 0;
for(int i = 1; i <= num_tot; i ++) {
id[i].pb(inf);
for(int j = 1; j <= tong[i]; j ++) id[i].pb(0), qu[++ tail] = node(i, j);
}
sort(qu+1, qu+1+tail, cmpnode);
for(int i = 1; i <= tail; i ++) id[qu[i].a][qu[i].b] = i;//预处理一个二元组(次数,颜色)排完序的编号
for(int i = 1; i < n; i ++) {
int x = read(), y = read();
addedge(x, y); addedge(y, x);
} bsz = sqrt(n); dfs(1, 0, 0);//树分块、倍增预处理
for(int j = 1; j <= 16; j ++)
for(int i = 1; i <= n; i ++) f[i][j] = f[f[i][j-1]][j-1];
for(int i = 1; i <= m; i ++) {
q[i].x = read(), q[i].y = read(), q[i].k = read();
q[i].id = i;
} sort(q+1, q+1+m, cmp);//查询预处理
//在(次数,颜色)这个二元组序列上分块
for(int i = 1; i <= tot; i ++) { bl[i] = br[i-1]+1; br[i] = br[i-1]+bsz; }
br[tot] = n;
for(int i = 1; i <= tot; i ++)
for(int j = bl[i]; j <= br[i]; j ++) bel[j] = i;
//以上是分块预处理
memset(tong, 0, sizeof tong);
int l = 1, r = 1;
for(int i = 1; i <= m; i ++) {
int x = q[i].x, y = q[i].y, z;
z = lca(l, x); change(l, z); change(x, z);
z = lca(r, y); change(r, z); change(y, z);
z = lca(x, y); work(z);
ans[q[i].id] = qry(q[i].k);
work(z); l = x, r = y;
}
for(int i = 1; i <= m; i ++) write(ans[i]), puts("");
return 0;
}