首页 > 其他分享 >线段树的分裂和合并(加强版)

线段树的分裂和合并(加强版)

时间:2022-08-24 22:48:57浏览次数:56  
标签:加强版 val int 线段 tr 分裂 救济粮 root top

上一篇文章讲了线段树分裂和合并的模板题,下面加强一下理解

题目链接:https://www.luogu.com.cn/problem/P4556

深绘里一直很讨厌雨天。

灼热的天气穿透了前半个夏天,后来一场大雨和随之而来的洪水,浇灭了一切。

虽然深绘里家乡的小村落对洪水有着顽固的抵抗力,但也倒了几座老房子,几棵老树被连根拔起,以及田地里的粮食被弄得一片狼藉。

无奈的深绘里和村民们只好等待救济粮来维生。

不过救济粮的发放方式很特别。

题目描述

首先村落里的一共有 n 座房屋,并形成一个树状结构。然后救济粮分 m 次发放,每次选择两个房屋 (x, y),然后对于 x 到 y 的路径上(含 x 和 y)每座房子里发放一袋 z 类型的救济粮。

然后深绘里想知道,当所有的救济粮发放完毕后,每座房子里存放的最多的是哪种救济粮。

输入格式

输入的第一行是两个用空格隔开的正整数,分别代表房屋的个数 n 和救济粮发放的次数 m。

第 2 到 第 n 行,每行有两个用空格隔开的整数 a, b,代表存在一条连接房屋 a 和 b 的边。

第 (n + 1) 到第 (n+m) 行,每行有三个用空格隔开的整数 x, y, z,代表一次救济粮的发放是从 x 到 y 路径上的每栋房子发放了一袋 z 类型的救济粮。

输出格式

输出 n 行,每行一个整数,第 i 行的整数代表 ii 号房屋存放最多的救济粮的种类,如果有多种救济粮都是存放最多的,输出种类编号最小的一种。

如果某座房屋没有救济粮,则输出 0。

 

这一道题主要算法有最近公共祖先,线段树合并,其次还有树上差分的思想

主要题意:有一个树形的村庄,每次操作会向x到y的路径的每一户村民发放一袋种类为z的救济粮,最后要求每一户村民家中的数量最多的救济粮的编号,如果数量相同,输出编号较小的

做法:向x到y路径的上每一户村民发放救济粮,可以利用树上差分,给x和y节点的线段树的z救济粮数量加一,x和y的最近公共祖先的线段树的救济粮减1,最近公共祖先的父节点减1,

最后dfs一遍,回溯的时候将子节点的线段树合并到父节点的线段树上去,维护区间最大值即可

那么为什么可以这样做呢,最后可以达到将x到y路径上的每一个点都加上1的效果

 

 

 

 

代码展示:

#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
const int N = 100010, M = N * 2;
int e[M], ne[M], h[N], idx;
int top[N], f[N], son[N], depth[N], sz[N];
int n, m, timestamp, tot;
int root[N], ans[N];

struct Node{
    int l, r;
    PII val;
}tr[N * 70];

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

void dfs1(int u, int fa)
{
    depth[u] = depth[fa] + 1, f[u] = fa, sz[u] = 1;
    int mxsz = 0;
    for(int i = h[u]; ~i; i = ne[i])
    {
        int j = e[i];
        if(j == fa) continue;
        dfs1(j, u);
        sz[u] += sz[j];
        if(mxsz < sz[j]) 
        {
            mxsz = sz[j];
            son[u] = j;
        }
    }
}

void dfs2(int u, int  t)
{
    top[u] = t;
    if(son[u]) dfs2(son[u], t);
    for(int i = h[u]; ~i; i = ne[i])
    {
        int j = e[i];
        if(j == f[u] || j == son[u]) continue;
        dfs2(j, j);
    }
}

int lca(int x,int y) //这里我用的是树链刨分求lca,当然也可以用倍增
{
    while(top[x] != top[y])
    {
        if(depth[top[x]] < depth[top[y]]) swap(x, y);
        x = f[top[x]];
    }
    return depth[x] < depth[y] ? x : y;
}

PII max(PII& x, PII& y) 
{
    if(x.first > y.first) return x;
    else if(x.first == y.first) return x.second < y.second ? x : y;
    else return y;
}

inline void pushup(int k) { tr[k].val = max(tr[tr[k].l].val, tr[tr[k].r].val); }

void modify(int &k, int l,int r,int p,int x)
{
    if(!k) k= ++ tot;
    if(l==r) 
    {
        tr[k].val.first += x; 
        tr[k].val.second = p; 
        return;
    }
    int m = (l+r)>>1;
    if(p<=m) modify(tr[k].l, l, m, p, x);
    else modify(tr[k].r, m+1, r, p, x);
    pushup(k);
}

void merge(int &x,int y,int l=1,int r=N) 
{
    if(!(x&&y))
        x|=y;
    else if(l==r) 
        tr[x].val.first += tr[y].val.first;
    else
    {
        int m = (l+r)>>1;
        merge(tr[x].l, tr[y].l, l, m);
        merge(tr[x].r, tr[y].r, m+1, r);
        pushup(x);
    }
}

void dfs(int u)
{
    for(int i = h[u]; ~i; i = ne[i])
    {
        int j = e[i];
        if(j == f[u]) continue;
        dfs(j);
        merge(root[u], root[j]);
    }
    if(tr[root[u]].val.first) ans[u] = tr[root[u]].val.second;
}

int main()
{
    cin >> n >> m;
    memset(h, -1, sizeof h);
    for(int i = 1; i < n; i ++ )
    {
        int a, b;
        cin >> a >> b;
        add(a, b), add(b, a);
    }
    
    dfs1(1, 0);
    dfs2(1, 0);
    
    while(m -- )
    {
        int x, y, z;
        cin >> x >> y >> z;
        int anc = lca(x, y);
        modify(root[x], 1, N, z, 1);
        modify(root[y], 1, N, z, 1);
        modify(root[anc], 1, N, z, -1);
        modify(root[f[anc]], 1, N, z, -1);
    }
    
    dfs(1);
    
    for(int i = 1; i <= n; i ++ ) cout << ans[i] << endl;
    
    return 0;
}

 

 

标签:加强版,val,int,线段,tr,分裂,救济粮,root,top
From: https://www.cnblogs.com/jay1573/p/16622513.html

相关文章

  • 线段树
    https://www.luogu.com.cn/problem/P33721#include<iostream>2#include<cstdio>3usingnamespacestd;4#definelsonl,mid,rt<<15#definersonmid+1,......
  • 势能线段树
    势能线段树什么是势能线段树所谓势能线段树,是指在懒标记无法正常使用的情况下,暴力到叶子将线段树当成数组一样用进行修改。大概就是先暴力,在暴力到一个状态的时候再用......
  • 线段树小结
    线段树线段树二分线段树分治可持久化线段树线段树合并线段树分裂线段树维护单调栈李超线段树势能线段树&吉司机线段树线段树套···都咕掉了。......
  • 李超线段树学习笔记
    这个hack数据是真的强。模板题的题解很重要哦,希望你能找到适合自己的。博客食用更佳哦李超线段树的定义对于李超线段树的定义,JHSeng大佬的定义简洁精炼:李超线段树是一......
  • #10007. 「一本通 1.1 练习 3」线段
    #include<bits/stdc++.h>usingnamespacestd;structnode{ intl,r;};boolcmp(nodex,nodey){ returnx.r<y.r;}classSolution{ public: intsolve(vector......
  • 开关(线段树区间异或板子)
    [TJOI2009]开关题目描述现有\(n\)盏灯排成一排,从左到右依次编号为:\(1\),\(2\),……,\(n\)。然后依次执行\(m\)项操作。操作分为两种:指定一个区间\([a,b]\),然后改变......
  • 172 树套树 线段树套平衡树
    视频链接:LuoguP3380【模板】二逼平衡树(树套树)//需要开O2#include<iostream>#include<algorithm>usingnamespacestd;#defineN2000010#defineINF21474836......
  • 线段树模板【带懒标记】
    #include<bits/stdc++.h>usingnamespacestd;typedeflonglongll;constintN=1e5+10;inta[N];structnode{  intl,r;  lladd,sum;  node(){  ......
  • YbtOJ 「数据结构」第4章 线段树
    不想dp了怎么办?开个新坑吧。例题1.求区间和树状数组不香吗,28行解决(bushi所以懒得打线段树了。code#include<bits/stdc++.h>#defineintlonglongusingnamespac......
  • 洛谷 P6242 【模板】线段树 3 吉司机线段树 区间取最小值 维护历史最大值和区间和
    题目背景本题是线段树维护区间最值操作与区间历史最值的模板。题目描述给出一个长度为 nn 的数列 AA,同时定义一个辅助数组 BB,BB 开始与 AA 完全相同。接下来......