必要的注释都已经加上了,包括线段树里面存的是什么
我下面这个代码正确性应该没问题,但是没有AC,因为我的空间复杂度还需要优化
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.HashMap;
public class Main {
static int N = 100010, M = 2 * N, n, m;
static int first[] = new int[N], ends[] = new int[M], next[] = new int[M], index = 0, maxjump = 18;
static int depth[] = new int[N], f[][] = new int[N][maxjump + 1], q[] = new int[N], head = 1, tail = 0;
public static void add(int a, int b){
index ++ ;
next[index] = first[a];
first[a] = index;
ends[index] = b;
}
public static void bfs(){
q[++ tail] = 1; depth[1] = 1;
while(tail - head + 1 > 0){
int u = q[head ++ ];
for(int p = first[u]; p > 0; p = next[p]){
int v = ends[p];
if(depth[v] != 0) continue;
depth[v] = depth[u] + 1; f[v][0] = u;
for(int k = 1; k <= maxjump; k ++ ) f[v][k] = f[f[v][k - 1]][k - 1];
q[++ tail] = v;
}
}
}
public static int lca(int a, int b){
if(depth[a] < depth[b]){
int k = a; a = b; b = k;
}
int needjump = depth[a] - depth[b];
int k = maxjump, jump = 1 << k;
while(needjump > 0){
if(needjump >= jump){
needjump -= jump;
a = f[a][k];
}
jump >>= 1; k -- ;
}
if(a == b) return a;
for(k = maxjump; k >= 0; k -- ){
if(f[a][k] != f[b][k]){
a = f[a][k]; b = f[b][k];
}
}
return f[a][0];
}
static class node {
int leftson, rightson, max, maxidx;
}
static node tr[] = new node[80 * N];
static int cnt = 0, root[] = new int[N];
/*
每一个节点有一棵线段树为他服务,比如说root[i]这棵线段树(其实是一棵以root[i]作为根节点的线段树,但是为了方便我们就这么说吧)
就是给i这个节点服务的,比如说这棵线段树里面有一个节点node叫做tr[u], 负责的区间是l ~ r,就是储存第l种 ~ r种救济粮的相关信息,那么是什么相关信息呢?
我们假设i这个节点有3个孩子,1 2 3,那么这3个孩子他们也会有第l种 ~ 第r种救济粮,固定一种救济粮,比如说是第k种救济粮,
那么就可以知道i的第k种救济粮比1 2 3这3个孩子的第k种救济粮加起来多了多少份,那么让k变动起来,我就可以得到l ~ r里面每一种救济粮,
i所拥有的比1 2 3这3个孩子所拥有的加起来多多少份了吧,而tr[u].max存的就是多得最多的那个,多了多少份,maxidx当然存的就是多的最多的那个编号是多少咯
*/
public static void update(int u, int l, int r, int x, int c){
// 这个说的是u这个节点他负责的区间是的l ~ r,然后这个区间里面x这个位置要多c
if(tr[u] == null) tr[u] = new node();
if(l == r){
// 走到这里的话其实l == r == x的
tr[u].max += c; tr[u].maxidx = l;
// 总共只有一种救济粮,那这一种不管你是多少,不管你比自己的孩子加起来的这一种多多少,这一种都是多得最多的
return;
}
int mid = (l + r) >> 1;
if(x <= mid){
if(tr[u].leftson == 0) tr[u].leftson = ++ cnt;
update(tr[u].leftson, l, mid, x, c);
}else{
if(tr[u].rightson == 0) tr[u].rightson = ++ cnt;
update(tr[u].rightson, mid + 1, r, x, c);
}
pushup(tr[u], tr[tr[u].leftson], tr[tr[u].rightson]);
}
public static void pushup(node father, node leftson, node rightson){
if(leftson == null){
father.max = rightson.max; father.maxidx = rightson.maxidx;
}else if(rightson == null){
father.max = leftson.max; father.maxidx = leftson.maxidx;
}else{
if(leftson.max >= rightson.max){
father.max = leftson.max; father.maxidx = leftson.maxidx;
// 这两个相等时候也是存leftson的,因为leftson的maxidx肯定是小于rightson的maxidx的,而题目里面有要求的,数量相同的话输出种类号偏小的
}else{
father.max = rightson.max; father.maxidx = rightson.maxidx;
}
}
}
public static void merge(int p, int q, int l, int r){
// 这个说的是我要把以p为根的线段树和以q为根的线段树进行合并,因为我之前是树上差分了嘛所以现在要合并
// 其实这题跟acwing1857那个最大流量的题差不多,那道题也是树上差分,最后合并,只不过那道题他只有“一种救济粮”,所以合并的时候是数组里面数字的加减
// 但是这题里面是若干种救济粮,合并要用线段树合并
if(tr[p] == null) tr[p] = new node();
if(tr[q] == null) return;
// 有一个为0的话说明根本没有这棵树,直接return就好了
if(l == r){
// 这个说的是p跟q负责的区间是l ~ r这个区间,如果只有单个点的话还是会合并的吧
tr[p].max += tr[q].max; tr[p].maxidx = l;
return;
// 其实合并非常简单,就是加起来就好
/*
所以你从这个式子就可以看出来我们其实是把q这棵线段树合并到p里面去
*/
}
int mid = (l + r) >> 1;
if(tr[q].leftson != 0){
if(tr[p].leftson == 0) tr[p].leftson = ++ cnt;
merge(tr[p].leftson, tr[q].leftson, l, mid);
}
if(tr[q].rightson != 0){
if(tr[p].rightson == 0) tr[p].rightson = ++ cnt;
merge(tr[p].rightson, tr[q].rightson, mid + 1, r);
}
pushup(tr[p], tr[tr[p].leftson], tr[tr[p].rightson]);
}
static int ans[] = new int[N];
public static void calres(){
for(int i = n; i >= 1; i -- ){
int u = q[i];
for(int p = first[u]; p > 0; p = next[p]){
int v = ends[p];
if(depth[v] != depth[u] + 1) continue;
if(root[u] == 0) root[u] = ++ cnt;
merge(root[u], root[v], 1, len);
}
if(root[u]!= 0 && tr[root[u]].max != 0) ans[u] = tr[root[u]].maxidx;
// 因为完全有可能某个节点他没有任何一种救济粮,这样的话maxidx会默认是1的,因为大家都是0嘛
// 这个时候答案应该是0才对,但是你默认是1,所以这里要特判一下
}
}
static class give{
int a, b, z;
}
static give op[] = new give[N];
static int b[] = new int[N], len = 0, t[] = new int[N];
static HashMap<Integer, Integer> map1 = new HashMap<>(), map2 = new HashMap<>();
public static void msort(int start, int end){
if(start == end ) return;
int mid = (start + end) >> 1;
msort(start, mid); msort(mid + 1, end);
int pointer1 = start, pointer2 = mid + 1;
for(int i = 0; i < end - start + 1; i ++ ){
if(pointer1 <= mid && pointer2 <= end){
if(b[pointer1] < b[pointer2]) t[i] = b[pointer1 ++ ];
else t[i] = b[pointer2 ++ ];
}else if(pointer1 <= mid) t[i] = b[pointer1 ++ ];
else t[i] = b[pointer2 ++ ];
}
for(int i = 0 ; i < end - start + 1; i ++ ) b[i + start] = t[i];
}
public static void unique(){
int cnt = 0;
for(int i = 1; i <= len; i ++ ){
if(map2.containsKey(b[i])) continue;
map1.put(++ cnt, b[i]);
map2.put(b[i], cnt);
}
len = map1.size();
}
static String s[];
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
public static void main(String args[]) throws Exception{
s = reader.readLine().split(" ");
n = Integer.parseInt(s[0]); m = Integer.parseInt(s[1]);
for(int i = 1; i <= n - 1; i ++ ){
s = reader.readLine().split(" ");
int a = Integer.parseInt(s[0]), b = Integer.parseInt(s[1]);
add(a, b); add(b, a);
}
bfs();
for(int i = 1; i <= m ; i ++ ){
s = reader.readLine().split(" ");
op[i] = new give();
op[i].a = Integer.parseInt(s[0]); op[i].b = Integer.parseInt(s[1]);
op[i].z = Integer.parseInt(s[2]);
b[++ len] = op[i].z;
}
msort(1, len); unique();
for(int i = 1; i <= m ; i ++ ){
int a = op[i].a, b = op[i].b, x = map2.get(op[i].z);
int ance = lca(a, b);
if(root[a] == 0) root[a] = ++ cnt;
if(root[b] == 0) root[b] = ++ cnt;
if(root[ance] == 0) root[ance] = ++ cnt;
if(root[f[ance][0]] == 0) root[f[ance][0]] = ++ cnt;
update(root[a], 1, len, x, 1);
update(root[b], 1, len, x, 1);
update(root[ance], 1, len, x, - 1);
update(root[f[ance][0]], 1, len, x, - 1);
}
calres();
map1.put(0, 0);
for(int i = 1; i <= n ; i ++ ) System.out.println(map1.get(ans[i]));
}
}
// 好了上面这个代码正确性应该没问题了,在洛谷跟acwing上面都过了12个数据点,60分及格了。在洛谷上面剩下的数据点都是TLE,然后在acwing上面是MLE了。
// 但是我懒得优化了。