首页 > 其他分享 >简明 线段树 指南

简明 线段树 指南

时间:2024-02-25 17:23:55浏览次数:32  
标签:指南 return int 线段 简明 len seg fk sum

洛谷博客链接

终于学会线段树了!!!

这篇博客将简单介绍 atcoder::lazy_segtree 的使用方法。

构造

lazy_segtree<S, op, e, F, mapping, composition, id> seg(n);

lazy_segtree<S, op, e, F, mapping, composition, id> seg(vector<T> a);


下面所有代码将用 \(01\) 序列,区间 \(\operatorname{XOR} 1\) 区间求和举例。

  • 线段树节点 \(S\):每个节点维护的信息。

上面的题目要求每个节点维护区间和以及区间长度。

struct S{int sum,len;};
  • 函数 S op(S x,S y)push_up 函数。

即合并两个线段树节点。

区间和和区间长度都是直接相加。

S op(S l,S r){return S{l.sum + r.sum,l.len + r.len};}
  • 函数 S e():返回初始值 \(e\)。

它要求任意元素 \(x\) 和 \(e\) 操作后返回值是 \(x\),例如取 \(\min\) 时 \(e = \inf\),本题中 \(e = \{0,0\}\)。

S e(){return S{0,0};}
  • 操作 \(F\):修改时进行的操作。

本题中 \(F =0/1\) 表示区间要异或的值。

using F = bool;
  • 函数 S mapping(F f,S x):返回元素 \(x\) 应用 \(f\) 操作后的值。

在本题里,异或 \(1\) 后 \(sum = len-sum\),异或 \(0\) 则不变。

S mapping(F f,S x){return S{(f ? x.len - x.sum : x.sum),x.len};}
  • 函数 F composition(F f,F g):操作之间的复合,返回 \(f(g(x))\)。

也就是两个 \(tag\) 的合并。在本题里,合并两个异或 \(tag\) 即把它们异或起来。

F composition(F f,F g){return (F)(f ^ g);}
  • 函数 F id():该函数应有 \(f(x) = x\)。

本题中返回 \(0\) 即可。

F id(){return 0;}

完整代码

using namespace atcoder;
int T,n;
struct S{int sum,len;};
using F = bool;
S op(S l,S r){return S{l.sum + r.sum,l.len + r.len};}
S e(){return S{0,0};}
S mapping(F f,S x){return S{(f ? x.len - x.sum : x.sum),x.len};}
F composition(F f,F g){return (F)(f ^ g);}
F id(){return 0;}
vector<S> a; int q;
void los(){
    cin >> n >> q; string s;
    cin >> s,s = " " + s;
    for(int i = 1;i <= n;i ++) a.push_back(S{s[i] - '0',1});
    lazy_segtree<S, op, e, F, mapping, composition, id> seg(a);
    while(q --){
        int po,l,r;
        if(cin >> po >> l >> r,!po) seg.apply(l - 1,r,F(1));
        else cout << seg.prod(l - 1,r).sum << "\n";
    }
}int main(){
    ios::sync_with_stdio(0),cin.tie(0);
    for(T = 1;T --;) los();
}

最近做的线段树题都会使用 atcoder::lazy_segtree,写完以后会把题目和代码贴在这里。

P2574 XOR的艺术

\(01\) 序列 \(a\),支持区间异或 \(1\),区间求和。

就是上面的例题。

ACLPC L - Lazy Segment Tree

\(01\) 序列 \(a\),支持区间异或 \(1\),区间求逆序对。

注意到 \(01\) 序列的逆序对只能是子序列 \(10\) 的数量,线段树维护区间 \(0,1,10\) 的数量。

using namespace atcoder;
const int N = 5e5+5;
using namespace std;
int T,n,q,tmp,po,l,r;
struct S{int x,y; ll ans;};
using F = bool;
S op(S l,S r){return S{l.x + r.x,l.y + r.y,l.ans + r.ans + 1ll * l.y * r.x};}
S e(){return S{0,0,0};}
S mapping(F f,S x){return !f ? x : S{x.y,x.x,1ll * x.x * x.y - x.ans};}
F composition(F f,F g){return F(f ^ g);}
F id(){return 0;}
vector<S> a;
void los(){
    cin >> n >> q;
    for(int i = 1;i <= n;i ++) cin >> tmp,a.emplace_back(!tmp,tmp,0);
    lazy_segtree<S, op, e, F, mapping, composition, id> seg(a);
    while(q --){
        if(cin >> po >> l >> r,po == 1) seg.apply(l - 1,r,F(1));
        else cout << seg.prod(l - 1,r).ans << "\n"; 
    }
}int main(){
    ios::sync_with_stdio(0),cin.tie(0);
    for(T = 1;T --;) los();
}

[ABC265G] 012 Inversion

给定 \(012\) 序列 \(a\),支持区间替换(\(0 \to x,1 \to y,2 \to z,x,y,z \in \{0,1,2\}\))和区间查逆序对。

比较恶心的题。

线段树节点记录 \(0,1,2,10,20,21\) 的数量,tag 维护 \(0,1,2\) 分别变成谁。修改时,\(0,1,2\) 的数量可以直接得到,原本的 \(10,20,21\) 直接贡献给 \(f_1f_0,f_2f_0,f_2f_1\),注意 \(01\) 也会贡献给 \(f_0f_1\)。\(01\) 的数量显然是原本的 \(0 \times 1 - 01\)。

合并两个 tag \(f,g\) 时,只需要返回 \(f_{g_i}\)。

#include <atcoder/lazysegtree>
using namespace atcoder;
struct S{
    ll r[6]; 
    ll operator [](int x){return r[x];};
};
struct F{
    int f[3];
    int operator [](int x){return f[x];};
};
S op(S l,S r){
    return S{{l[0] + r[0],l[1] + r[1],l[2] + r[2],
        l[3] + r[3] + l[1] * r[0],l[4] + r[4] + l[2] * r[0],l[5] + r[5] + l[2] * r[1]}};
}S e(){return S{0,0,0,0,0,0};}
S mapping(F f,S x){
    vector<vector<ll>> fk(3,vector<ll>(3)); vector<ll> sb(3);
    fk[f[0]][f[0]] += x[0],fk[f[1]][f[1]] += x[1],fk[f[2]][f[2]] += x[2],
    fk[f[1]][f[0]] += x[3],fk[f[2]][f[0]] += x[4],fk[f[2]][f[1]] += x[5],
    fk[f[0]][f[1]] += x[0] * x[1] - x[3],fk[f[0]][f[2]] += x[0] * x[2] - x[4],
    fk[f[1]][f[2]] += x[1] * x[2] - x[5],sb[f[0]] += x[0],sb[f[1]] += x[1],sb[f[2]] += x[2];
    return S{sb[0],sb[1],sb[2],fk[1][0],fk[2][0],fk[2][1]};
}F composition(F f,F g){
    return F{f[g[0]],f[g[1]],f[g[2]]};
}F id(){return {0,1,2};}
int T,n,q,x,po,l,r,s1,s2,s3;
vector<S> a;
void los(){
    cin >> n >> q;
    for(int i = 1;i <= n;i ++) cin >> x,a.push_back({(x == 0),(x == 1),(x == 2),0,0,0});
    lazy_segtree<S, op, e, F, mapping, composition, id> seg(a);
    while(q --){
        if(cin >> po >> l >> r,po == 1){
            auto s = seg.prod(l - 1,r);
            cout << s.r[3] + s.r[4] + s.r[5] << "\n";
        }else cin >> s1 >> s2 >> s3,seg.apply(l - 1,r,F{s1,s2,s3});
    }
}int main(){
    ios::sync_with_stdio(0),cin.tie(0);
    for(T = 1;T --;) los();
}

参考文献

AClibrary Lazy Segtree 官方文档

像使用stl一样使用线段树——区间修改 与 实例

标签:指南,return,int,线段,简明,len,seg,fk,sum
From: https://www.cnblogs.com/sunkuangzheng/p/18032630

相关文章

  • 洛谷 P4198 楼房重建(线段树上二分)
    传送门解题思路动态维护区间里面单调递增斜率的长度需要维护两个信息:上述长度,和区间最大值(合并时需要)难点在于两个子区间的合并。左区间的楼房一定都能看见(没有遮挡),所以要在右区间二分,找到左面最大值lmax在右区间的位置,然后进行合并。复杂度两个log。AC代码#include<ios......
  • POJ--3468 A Simple Problem with Integers(线段树/树状数组)
    记录11:032024-2-25http://poj.org/problem?id=1961线段树树状数组把区间增加转变为单点增加,利用两个树状数组\(c_0和c_1\)将”Clrd"转化为在树状数组\(c_0\)中,把位置l上的数加d在树状数组\(c_0\)中,把位置r+1上的数减d在树状数组\(c_1\)中,把位置l上的数......
  • 洛谷题单指南-贪心-P1208 [USACO1.3] 混合牛奶 Mixing Milk
    原题链接:https://www.luogu.com.cn/problem/P1208题意解读:就是一个部分背包问题,贪心模版题。解题思路:优先选择单价低的牛奶即可。100分代码:#include<bits/stdc++.h>usingnamespacestd;constintN=5005;structfarmer{intprice,count;}f[N];boolcmp(fa......
  • 洛谷题单指南-贪心-P5019 [NOIP2018 提高组] 铺设道路
    原题链接:https://www.luogu.com.cn/problem/P5019题意解读:最短时间内填满道路,连在一起的不为0的坑可以一起填解题思路:方法1:分治法对于一段连续不同深度的坑,可以最多连续填的天数是最小深度在填满最小深度之后,分别针对其左边和右边的区域再进行填充,这就是此题分治法的理论基......
  • WPF大展示专业指南:轻松实现多屏显示的绝技
     概述:WPF通过System.Windows.Forms.Screen类,实现多屏显示轻而易举。通过获取屏幕信息、确定窗体位置和设置窗体大小,可在大型展示或数据可视化应用中灵活利用多屏幕。示例代码清晰演示了如何在WPF中实现这一功能。在WPF(WindowsPresentationFoundation)中,实现多屏显示可以通过......
  • git使用指南
    1.基础操作1.1初始化repogitinit1.2添加更改gitadd1.3添加到暂存区gitcommit-m"update"1.4克隆仓库gitclone2.版本管理2.1查看repo状态gitstatus2.2查看文件变化gitdiff2.3查看当前版本的loggitlog2.4查看所有的loggitreflog2.5版本回退g......
  • 这就是我们的李超线段树啊,你们有没有这样的李超线段树啊?
    沟槽的公式,真是公公又式式啊。考虑一个线段树节点维护一个线段(但一条线段可以被多个线段树节点维护),需要保证该节点被线段完全覆盖。每次添加一个线段的时候:如果当前节点没有被这个线段完全覆盖,那么直接递归左右儿子修改。如果当前节点的线段比新线段严格劣(也就是对于每一......
  • Git 版本控制系统的完整指南
    什么是Git?Git是一个流行的版本控制系统。它是由LinusTorvalds于2005年创建的,自那时以来由JunioHamano维护。它用于:跟踪代码更改跟踪谁做出了更改编写协作Git做什么?使用仓库管理项目克隆项目以在本地副本上工作使用暂存和提交来控制和跟踪更改分支和合并允......
  • containerd环境搭建指南
    目录一.container概述1.什么是containerd2.为什么要学习containerd二.基于yum方式安装containerd1.获取软件源2.查看yum源中containerd软件版本3.安装containerd的4.查看containerd的版本信息5.设置containerd开机自启动6.查看containerd的客户端和服务端的版本信息三.基于二进制......
  • 线段树分治&cdq分治&整体二分
    preface感觉三种分治算法容易搞混并不容易区分它们使用的场景和题目(虽然有些题目根据性质可以使用多种分治),所以还是要归纳一下线段树分治Part1主要是处理一类带有撤回的问题,也就是一次修改只对一段区间生效(这里的区间指的是时间)即区间修改,单点查询流程大致是把区间修改挂在......