社区讨论

请求大佬救援,线段树合并模板题

学术版参与者 2已保存回复 4

讨论操作

快速查看讨论及其快照的属性,并进行相关操作。

当前回复
4 条
当前快照
1 份
快照标识符
@mhjnylmi
此快照首次捕获于
2025/11/04 05:40
4 个月前
此快照最后确认于
2025/11/04 05:40
4 个月前
查看原帖
线段树合并模板代码有一点点注释方便看,目前又是WA又是MLE又是RE的,只有30分。麻烦大老帮看看,谢谢。
CPP
#include<bits/stdc++.h>
using namespace std;
#define N 200010
int n, m, ans[N];
int tmp=0, head[N];
int sum[N<<2], ls[N<<2], rs[N<<2], c[N<<2];
struct edge{
    int to, next;
}e[N<<4];
void add(int u, int v){
    e[++tmp] = (edge){v, head[u]};
    head[u] = tmp;
}
int node=0, root[N], d[N], siz[N], fa[N], son[N];
void dfs1(int x, int f){//树链剖分准备求LCA
    d[x] = d[f]+1;
    fa[x] = f;
    siz[x] = 1;
    for(int i=head[x]; i; i=e[i].next){
        int y=e[i].to;
        if(y == f) continue;
        dfs1(y, x);
        siz[x] += siz[y];
        if(!son[x || siz[son[x]]<siz[y]])
            son[x] = y;
    }
}
int cnt=0, id[N], tp[N];
void dfs2(int x, int top){
    id[++cnt] = x;
    tp[x] = top;
    if(!son[x]) return ;
    dfs2(son[x], top);
    for(int i=head[x]; i; i=e[i].next){
        int y=e[i].to;
        if(y==fa[x] || y==son[x]) continue;
        dfs2(y, y);
    }
}
int LCA(int x, int y){
    while(tp[x] != tp[y]){
        if(d[tp[x]] < d[tp[y]]) swap(x, y);
        x = fa[tp[x]];
    }
    return d[x]<d[y]?x:y;
}
void pushup(int p){//当前节点货物最多 = max(左儿子最多, 右儿子最多)
    if(sum[ls[p]] >= sum[rs[p]]){//等于号钦定了当个数相同时输出小的,权值线段树越左边越小
        sum[p] = sum[ls[p]];
        c[p] = c[ls[p]];
    }
    else{
        sum[p] = sum[rs[p]];
        c[p] = c[rs[p]];
    }
    return ;
}
void modify(int &p, int l, int r, int pos, int val){
    if(!p) p=++node;//动态开点
    if(l==r){
        sum[p]+=val;
        if(sum[p]!=0) c[p]=l;
        return ;
    }
    int mid = l+r>>1;
    if(pos<=mid) modify(ls[p], l, mid, pos, val);
    if(pos>mid) modify(rs[p], mid+1, r, pos, val);
    pushup(p);
    return ;
}
int merge(int x, int y, int l, int r){
    if(!x || !y) return x+y;
    if(l == r){
        sum[x] += sum[y];
        if(sum[x]!=0) c[x] = l;
        return x;
    }
    int mid = l+r>>1;
    ls[x] = merge(ls[x], ls[y], l, mid);
    rs[x] = merge(rs[x], rs[y], mid+1, r);
    pushup(x);
    return x;
}
void calc(int x){
    for(int i=head[x]; i; i=e[i].next){
        int y = e[i].to;
        if(y==fa[x]) continue;
        calc(y);
        root[x] = merge(root[x], root[y], 1, N);
    }
    if(sum[root[x]]==0) ans[x]=0;
    else ans[x] = c[root[x]];
    return ;
}
int main()
{
    //freopen("P4556.in", "r", stdin);
    //freopen("P4556.out", "w", stdout);
    scanf("%d%d", &n, &m);
    for(int i=1; i<=n-1; i++){
        int u, v;
        scanf("%d%d", &u, &v);
        add(u, v); add(v, u);
    }
    dfs1(1, 0);
    dfs2(1, 1);
    //对于原图的每一个节点,都建一棵权值线段树
    while(m--){
        int x, y, z;
        scanf("%d%d%d", &x, &y, &z);
        int lca = LCA(x, y);
        modify(root[x], 1, N, z, 1);
        modify(root[fa[lca]], 1, N, z, -1);
        if(x!=y){
            modify(root[y], 1, N, z, 1);
            modify(root[lca], 1, N, z, -1);
        }//树上差分的技巧, 最终的答案是当前节点整个子树的权值线段树合并起来后再算最多粮食种类
    }
    calc(1);
    for(int i=1; i<=n; i++) printf("%d\n", ans[i]);
    return 0;
}

回复

4 条回复,欢迎继续交流。

正在加载回复...