P4556 雨天的尾巴 线段树合并

使用线段树合并,每个节点维护一棵权值线段树,下标为救济粮种类,区间维护数量最多的救济粮编号(下标)。所以每个节点答案即为\(tre[rot[x]]\)。

然后运用树上点的差分思想,对于分发路径\(u,v\),我们在\(u\)上+1,在\(v\)+1,在\(lca(u,v)\)处-1,在\(fa(lca)\)处-1,最后统计时自底向上做树上前缀和、线段树合并即得当前节点信息。

需要注意的是,在合并时可能会出现\(tre[rot[x]]\)不为\(0\),但是\(sum[rot[x]]\)为\(0\)的情况,这时候表示编号为\(tre[rot[x]]\)的救济粮数量为\(0\),所以此时应该将\(ans[x]\)更新为\(0\)(根据题意)。

#include <cstdio>
#include <algorithm>
#define MAXN 100001
#define LOG 19
#define mxr 100000
using namespace std;
inline int read(){
    char ch=getchar();int s=0;
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') s=s*10+(ch^'0'),ch=getchar();
    return s;
}
int head[MAXN],nxt[MAXN*2],vv[MAXN*2],tot;
inline void add_edge(int u, int v){
    vv[++tot]=v;
    nxt[tot]=head[u];
    head[u]=tot;
}
int n,m;
#define MAXM MAXN*20
int tre[MAXM],sum[MAXM],sl[MAXM],sr[MAXM],cnt;
void push_up(int x){
    if(sl[x]==0){
        sum[x]=sum[sr[x]];
        tre[x]=tre[sr[x]];
        return;
    }
    if(sr[x]==0){
        sum[x]=sum[sl[x]];
        tre[x]=tre[sl[x]];
        return;
    }
    if(sum[sl[x]]>=sum[sr[x]]){
        sum[x]=sum[sl[x]];
        tre[x]=tre[sl[x]];
    }else{
        sum[x]=sum[sr[x]];
        tre[x]=tre[sr[x]];
    }
}
void change(int &x, int l, int r, int pos, int val){
    if(x==0) x=++cnt;
    if(l==r){
        sum[x]+=val;
        tre[x]=pos;
        return;
    }
    int mid=(l+r)>>1;
    if(pos<=mid) change(sl[x], l, mid, pos, val);
    else change(sr[x], mid+1, r, pos, val);
    push_up(x);
}
int merge(int a, int b, int l, int r){
    if(a==0||b==0) return a+b;
    if(l==r){
        sum[a]+=sum[b];
        return a;
    }
    int mid=(l+r)>>1;
    sl[a]=merge(sl[a], sl[b], l, mid);
    sr[a]=merge(sr[a], sr[b], mid+1, r);
    push_up(a);
    return a;
}
int f[MAXN][LOG],dep[MAXN];
void dfs(int u, int fa){
    f[u][0]=fa;
    dep[u]=dep[fa]+1;
    for(int i=1;i<LOG;++i)
        f[u][i]=f[f[u][i-1]][i-1];
    for(int i=head[u];i;i=nxt[i]){
        int v=vv[i];
        if(v==fa) continue;
        dfs(v, u);
    }
}
int lca(int a, int b){
    if(dep[a]<dep[b]) swap(a, b);
    for(int i=LOG-1;i>=0;--i)
        if(dep[f[a][i]]>=dep[b])
            a=f[a][i];
    if(a==b) return a;
    for(int i=LOG-1;i>=0;--i)
        if(f[a][i]!=f[b][i])
            a=f[a][i],b=f[b][i];
    return f[a][0];
}
int rot[MAXN],ans[MAXN];
void calc(int u, int fa){
    for(int i=head[u];i;i=nxt[i]){
        int v=vv[i];
        if(v==fa) continue;
        calc(v, u);
        rot[u]=merge(rot[u], rot[v], 1, mxr);
    }
    ans[u]=tre[rot[u]];
    if(sum[rot[u]]==0) ans[u]=0; // 特判没有救济粮的情况
}
int main(){
    n=read(),m=read();
    for(int i=1;i<n;++i){
        int a=read(),b=read();
        add_edge(a, b);
        add_edge(b, a);
    }
    dfs(1, 0);
    for(int i=1;i<=m;++i){
        int a=read(),b=read(),x=read();
        int l=lca(a, b);
        change(rot[a], 1, mxr, x, 1);
        change(rot[b], 1, mxr, x, 1);
        change(rot[l], 1, mxr, x, -1);
        change(rot[f[l][0]], 1, mxr, x, -1);
    }
    calc(1, 0);
    for(int i=1;i<=n;++i) printf("%d\n", ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/santiego/p/11771159.html

时间: 2024-11-09 01:21:40

P4556 雨天的尾巴 线段树合并的相关文章

luogu4556 雨天的尾巴 (线段树合并+差分)

题目背景 深绘里一直很讨厌雨天.灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切.虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮食被弄得一片狼藉.无奈的深绘里和村民们只好等待救济粮来维生.不过救济粮的发放方式很特别.题目描述 首先村落里的一共有n座房屋,并形成一个树状结构.然后救济粮分m次发放,每次选择两个房屋(x,y),然后对于x到y的路径上(含x和y)每座房子里发放一袋z类型的救济粮.然后深绘里想知道,当所有的救济粮发

BZOJ 3307 雨天的尾巴 线段树

题目大意:给定一棵树,有m个操作,每次给一条路径上的每个点分发一个颜色为z的物品,所有操作结束后输出每个点上数量最多的是哪种物品 对于每个操作,我们在x和y上各打上一个插入z的标记,然后在LCA(x,y)和Fa(LCA(x,y))上各打上一个删除z的标记 然后我们对z维护线段树 DFS一遍,对于每个节点进行如下操作: 1.将所有子节点的线段树合并过来 2.处理标记,该插♂入的插♂入,该删除的删除 3.查询最大值作为答案 这样的复杂度是O(mlogm)的 然而跑不过树链剖分什么鬼--是我没离散化的

P4556 [Vani有约会]雨天的尾巴 树链剖分 线段树合并

P4556 [Vani有约会]雨天的尾巴 提交2.75k 通过789 时间限制1.00s 内存限制125.00MB 提交代码加入收藏 题目提供者yyy2015c01 难度省选/NOI- 历史分数100 提交记录查看题解 标签 查看算法标签 相关讨论 进入讨论版 查看讨论 推荐题目 查看推荐 展开 题目背景 深绘里一直很讨厌雨天.灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切.虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮

P4556 [Vani有约会]雨天的尾巴(线段树合并)

传送门 一道线段树合并 首先不难看出树上差分 我们把每一次修改拆成四个,在\(u,v\)分别放上一个,在\(lca\)和\(fa[lca]\)各减去一个,那么只要统计一下子树里的总数即可 然而问题就在于怎么统计.直接暴力肯定是要咕咕的,那么线段树合并就派上用场了 总之就是每个点开一个动态开点线段树,然后一遍dfs,让它的所有儿子的线段树合并到它这里 我按以前的写法不知为什么写挂了--然后换抄了种写法还是挂--后来发现是写抄的时候没有注意合并的顺序-- //minamoto #include<bi

[树上差分][线段树合并]JZOJ 3397 雨天的尾巴

Description 深绘里一直很讨厌雨天. 灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切. 虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连 根拔起,以及田地里的粮食被弄得一片狼藉. 无奈的深绘里和村民们只好等待救济粮来维生. 不过救济粮的发放方式很特别. 首先村落里的一共有n 座房屋,并形成一个树状结构.然后救济粮分m 次发放,每次选择 两个房屋(x,y),然后对于x 到y 的路径上(含x 和y) 每座房子里发放一袋z 类型的救济粮.

bzoj3307雨天的尾巴(可持久化线段树合并)

题目大意: 一颗树,想要在树链上添加同一物品,问最后每个点上哪个物品最多. 解题思路: 假如说物品数量少到可以暴力添加,且树点极少,我们怎么做. 首先在一个树节点上标记出哪些物品有多少,寻找道路上的节点暴力添加. 而如果节点比较多,我们使用树上差分u+1,v+1,lca-1,fa[lca]-1向上求和就得到了答案 而颜色较多呢,和刚才唯一不同就在于向上求和时不要用数组,用线段树合并就好了(记录线段树区间的最大值与最大位置) 在神犇博客那里看到了废点的回收^_^ 代码: 1 #include<qu

Luogu4556 雨天的尾巴 树上差分、线段树合并

传送门 一个套路题-- 树上差分+线段树合并即可 注意一个细节:$pushup$的时候如果最大值为$0$一定要把最大值对应的答案也设为$0$,否则会$WA$第二个点 另外如果这道题空限变小了请不要交这份代码,因为这份代码没有卡空间... 1 #include<bits/stdc++.h> 2 #define mid ((l + r) >> 1) 3 //This code is written by Itst 4 using namespace std; 5 6 inline in

线段树合并:从入门到放弃

感谢这篇博客(这里跳转)以及邱宇大神的讲解,我也算作入门(自闭)了. 需要掌握的前置知识点:动态开点线段树.权值线段树. 一.合并思想 线段树合并,就是指建立一颗新的线段树,保存原有的两颗线段树的信息. 那么就是: 假设现在合并到了线段树的a.b某一个pos 如果a在这个区间有pos,b没有,那么新的线段树pos位置赋值为a 如果b在这个区间有pos,a没有,那么新的线段树pos位置赋值为b 如果a.b都有,那么继续 如果此时处理到了两颗线段树的叶子节点,就把b在pos的值加到a上,把新线段树上

GDOI模拟雨天的尾巴【树套树】

雨天的尾巴 深绘里一直很讨厌雨天.灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切.虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮食被弄得一片狼藉.无奈的深绘里和村民们只好等待救济粮来维生.不过救济粮的发放方式很特别.首先村落里的一共有n 座房屋,并形成一个树状结构.然后救济粮分m 次发放,每次选择两个房屋(x,y),然后对于x 到y 的路径上(含x 和y) 每座房子里发放一袋z 类型的救济粮.然后深绘里想知道,当所有的救