洛谷官方题单--线段树

· 浏览次数 : 7

小编点评

前言 发现线段树根本不会写,所以想要完成洛谷官方题单来稍微提升一下... 持续更新ing P3870 [TJOI2009] 开关 明确了写线段树要思考的几个点: 1. 如何update,即如何合并子节点的信息,这里就是直接将子节点的灯的数量相加即可 2. 如何modify,即如何根据tag修改该节点的值,这里就是节点长度-灯的数量就行,还有如何下传tag,这里直接^就行 神奇的代码 ```cpp #include #include #include #include #include #include #include #include using namespace std; #define endl '\' typedef long long LL; typedef unsigned long long ULL; typedef pair PII; const int INF = 0x3f3f3f3f; const int N = 1e5 + 10; struct Node { int add; int x; } seg[N * 4]; Node operator+(const Node &a, const Node &b) { Node c; c.x = a.x + b.x; return c; } void settag(int u, int l, int r, int add) { if (add == 1) { int len = r - l + 1; seg[u].x = len - seg[u].x; seg[u].add ^= add; } } void pushdown(int u, int l, int r) { int mid = l + r >> 1; settag(u * 2, l, mid, seg[u].add); settag(u * 2 + 1, mid + 1, r, seg[u].add); seg[u].add = 0; } void update(int u) { seg[u].x = seg[u * 2].x + seg[u * 2 + 1].x; } void modify(int u, int l, int r, int ql, int qr, int add) { assert(l <= ql and qr <= r); if (l == ql and r == qr) { settag(u, l, r, add); } else { pushdown(u, l, r); int mid = l + r >> 1; if (qr <= mid) modify(u * 2, l, mid, ql, qr, add); else if (ql >= mid + 1) modify(u * 2 + 1, mid + 1, r, ql, qr, add); else { modify(u * 2, l, mid, ql, mid, add); modify(u * 2 + 1, mid + 1, r, mid + 1, qr, add); } update(u); } } Node query(int u, int l, int r, int ql, int qr) { assert(l <= ql and qr <= r); if (l == ql and r == qr) return seg[u]; else { pushdown(u, l, r); int mid = l + r >> 1; if (qr <= mid) return query(u * 2, l, mid, ql, qr); else if (ql >= mid + 1) return query(u * 2 + 1, mid + 1, r, ql, qr); else { auto L = query(u * 2, l, mid, ql, mid); auto R = query(u * 2 + 1, mid + 1, r, mid + 1, qr); Node res = L + R; return res; } } update(u); } void solve() { int n, m; cin >> n >> m; while (m--) { int c, a, b; cin >> c >> a >> b; if (c == 0) { modify(1, 1, n, a, b, 1); } else { cout<< query(1, 1, n, a, b).x<< endl; } } } int main() { ios::sync_with_stdio(false); cin.tie(0); int T = 1; // cin >> T; while (T--) { solve(); } return 0; } ``` P1438 无聊的数列 区间加等差数列,单点求和直接转化为差分,区间加就行 用树状数组也能做 神奇的代码 ```cpp #include #include #include #include #include #include #include #include using namespace std; #define endl '\' typedef long long LL; typedef unsigned long long ULL; typedef pair PII; const int INF = 0x3f3f3f3f; const int N = 1e5 + 10; LL a[N], b[N]; struct Node { LL add; LL x; } seg[N * 4]; void update(int u) { seg[u].x = seg[u * 2].x + seg[u * 2 + 1].x; } void build(int u, int l, int r) { assert(l <= r); if (l == r) seg[u].x = b[l]; else { int mid = l + r >> 1; build(u * 2, l, mid); build(u * 2 + 1, mid + 1, r); update(u); } } void settag(int u, int l, int r, LL add) { int len = r - l + 1; seg[u].x += len * add; seg[u].add += add; } void pushdown(int u, int l, int r) { int mid = l + r >> 1; settag(u * 2, l, mid, seg[u].add); settag(u * 2 + 1, mid + 1, r, seg[u].add); seg[u].add = 0; } void modify(int u, int l, int r, int ql, int qr, LL add) { assert(l <= ql and qr <= r); if (l == ql and r == qr) { settag(u, l, r, add); } else { pushdown(u, l, r); int mid = l + r >> 1; if (qr <= mid) modify(u * 2, l, mid, ql, qr, add); else if (ql >= mid + 1) modify(u * 2 + 1, mid + 1, r, ql, qr, add); else { modify(u * 2, l, mid, ql, mid, add); modify(u * 2 + 1, mid + 1, r, mid + 1, qr, add); } update(u); } } Node query(int u, int l, int r, int ql, int qr) { assert(l <= ql and qr <= r); if (l == ql and r == qr) return seg[u]; else { pushdown(u, l, r); int mid = l + r >> 1; if (qr <= mid) return query(u * 2, l, mid, ql, qr); else if (ql >= mid + 1) return query(u * 2 + 1, mid + 1, r, ql, qr); else { Node res; auto L = query(u * 2, l, mid, ql, mid); auto R = query(u * 2 + 1, mid + 1, r, mid + 1, qr); res.x = L.x + R.x; return res; } } update(u); } void solve() { int n, m; cin >> n >> m; for (int i = 1; i <= n; i++) cin >> a[i]; for (int i = 1; i <= n; i++) b[i] = a[i] - a[i - 1]; build(1, 1, n); while (m--) { int op; cin >> op; if (op == 1) { LL l, r, k, d; cin >> l >> r >> k >> d; modify(1, 1, n, l, l, k); if (l + 1 < r) modify(1, 1, n, l + 1, r, -(k + (r - l) * d)); if (r + 1 < n) modify(1, 1, n, r + 1, r + 1, -(k + (r - l) * d)); } else { int p; cin >> p; cout<< query(1, 1, n, 1, p).x<< endl; } } } int main() { ios::sync_with_stdio(false); cin.tie(0); int T = 1; // cin >> T; while (T--) { solve(); } return 0; } ``` 归纳总结以上内容,生成内容时需要带简单的排版

正文

前言

发现线段树根本不会写,所以想要完成洛谷官方题单来稍微提升一下...
持续更新ing

[========]

P3870 [TJOI2009] 开关
明确了写线段树要思考的几个点
1.如何update,即如何合并子节点的信息,这里就是直接将子节点的灯的数量相加即可
2.如何modify,即如何根据tag修改该节点的值,这里就是节点长度-灯的数量就行
还有如何下传tag,这里直接^就行

神奇的代码
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<cstring>
#include<assert.h>

using namespace std;

#define endl '\n'
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
const int INF=0x3f3f3f3f;

const int N=1e5+10;
struct Node{
    int add;
    int x;
}seg[N*4];

Node operator+(const Node& a,const Node& b){
    Node c;
    c.x=a.x+b.x;
    return c;
}

void settag(int u,int l,int r,int add){
    if(add==1){
        int len=r-l+1;
        seg[u].x=len-seg[u].x;
        seg[u].add^=add;
    }
}

void pushdown(int u,int l,int r){
    int mid=l+r>>1;
    settag(u*2,l,mid,seg[u].add);
    settag(u*2+1,mid+1,r,seg[u].add);
    seg[u].add=0;
}

void update(int u){
    seg[u].x=seg[u*2].x+seg[u*2+1].x;
}

void modify(int u,int l,int r,int ql,int qr,int add){
    assert(l<=ql and qr<=r);
    if(l==ql and r==qr)settag(u,l,r,add);
    else{
        pushdown(u,l,r);
        int mid=l+r>>1;
        if(qr<=mid)modify(u*2,l,mid,ql,qr,add);
        else if(ql>=mid+1)modify(u*2+1,mid+1,r,ql,qr,add);
        else{
            modify(u*2,l,mid,ql,mid,add);
            modify(u*2+1,mid+1,r,mid+1,qr,add);
        }
        update(u);
    }
}

Node query(int u,int l,int r,int ql,int qr){
    assert(l<=ql and qr<=r);
    if(l==ql and r==qr)return seg[u];
    else{
        pushdown(u,l,r);
        int mid=l+r>>1;
        if(qr<=mid)return query(u*2,l,mid,ql,qr);
        else if(ql>=mid+1)return query(u*2+1,mid+1,r,ql,qr);
        else{
            auto L=query(u*2,l,mid,ql,mid);
            auto R=query(u*2+1,mid+1,r,mid+1,qr);
            Node res=L+R;
            return res;
        }
        update(u);
    }
}

void solve(){
    int n,m;
    cin>>n>>m;

    while(m--){
        int c,a,b;
        cin>>c>>a>>b;
        if(c==0){
            modify(1,1,n,a,b,1);
        }else{
            cout<<query(1,1,n,a,b).x<<endl;
        }
    }
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T = 1;
    // cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

[========]

P1438 无聊的数列
区间加等差数列,单点求和
直接转化为差分,区间加就行
用树状数组也能做

神奇的代码
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<cstring>
#include<assert.h>

using namespace std;

#define endl '\n'
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;
const int INF=0x3f3f3f3f;

const int N=1e5+10;
LL a[N],b[N];
struct Node{
    LL add;
    LL x;
}seg[N*4];

void update(int u){
    seg[u].x=seg[u*2].x+seg[u*2+1].x;
}

void build(int u,int l,int r){
    assert(l<=r);
    if(l==r)seg[u].x=b[l];
    else{
        int mid=l+r>>1;
        build(u*2,l,mid);
        build(u*2+1,mid+1,r);
        update(u);
    }
}

void settag(int u,int l,int r,LL add){
    int len=r-l+1;
    seg[u].x+=len*add;
    seg[u].add+=add;    
}

void pushdown(int u,int l,int r){
    int mid=l+r>>1;
    settag(u*2,l,mid,seg[u].add);
    settag(u*2+1,mid+1,r,seg[u].add);
    seg[u].add=0;
}

void modify(int u,int l,int r,int ql,int qr,LL add){
    assert(l<=ql and qr<=r);
    if(l==ql and r==qr)settag(u,l,r,add);
    else{
        pushdown(u,l,r);
        int mid=l+r>>1;
        if(qr<=mid)modify(u*2,l,mid,ql,qr,add);
        else if(ql>=mid+1)modify(u*2+1,mid+1,r,ql,qr,add);
        else{
            modify(u*2,l,mid,ql,mid,add);
            modify(u*2+1,mid+1,r,mid+1,qr,add);
        }
        update(u);
    }
}

Node query(int u,int l,int r,int ql,int qr){
    assert(l<=ql and qr<=r);
    if(l==ql and r==qr)return seg[u];
    else{
        pushdown(u,l,r);
        int mid=l+r>>1;
        if(qr<=mid)return query(u*2,l,mid,ql,qr);
        else if(ql>=mid+1)return query(u*2+1,mid+1,r,ql,qr);
        else{
            Node res;
            auto L=query(u*2,l,mid,ql,mid);
            auto R=query(u*2+1,mid+1,r,mid+1,qr);
            res.x=L.x+R.x;
            return res;
        }
        update(u);
    }
}

void solve(){
    int n,m;cin>>n>>m;
    for(int i=1;i<=n;i++)cin>>a[i];
    for(int i=1;i<=n;i++)b[i]=a[i]-a[i-1];
    build(1,1,n);

    while(m--){
        int op;cin>>op;
        if(op==1){
            LL l,r,k,d;
            cin>>l>>r>>k>>d;
            modify(1,1,n,l,l,k);
            if(l+1<=r)modify(1,1,n,l+1,r,d);
            if(r+1<=n)modify(1,1,n,r+1,r+1,-(k+(r-l)*d));
        }else{
            int p;cin>>p;
            cout<<query(1,1,n,1,p).x<<endl;
        }
    }  
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int T = 1;
    // cin>>T;
    while(T--){
        solve();
    }
    return 0;
}

与洛谷官方题单--线段树相似的内容:

洛谷官方题单--线段树

前言 发现线段树根本不会写,所以想要完成洛谷官方题单来稍微提升一下... 持续更新ing [ ] P3870 [TJOI2009] 开关 明确了写线段树要思考的几个点 1.如何update,即如何合并子节点的信息,这里就是直接将子节点的灯的数量相加即可 2.如何modify,即如何根据tag修改该节

洛谷P2433 小学数学 N 合一

写完了这道题结果脑子断电把浏览器关了。。。。。。打开一看 没保存 寄 传送门:【深基1-2】小学数学 N 合一 - 洛谷 第一题 第二题 第三题 这几道题没啥好说的,直接输出就彳亍了 cout << "I love Luogu!" << endl; cout << “6 4” << endl; co

洛谷题解 | P1051 谁拿了最多奖学金

​目录 题目描述 输入格式 输出格式 输入输出样例 提示 题目思路 AC代码 题目描述 某校的惯例是在每学期的期末考试之后发放奖学金。发放的奖学金共有五种,获取的条件各自不同: 1. 院士奖学金,每人 8000 元,期末平均成绩高于 80 分(>80),并且在本学期内发表1篇或1篇以上论文的学生均可

洛谷题解 | P5660 数字游戏

​ 目录 题目描述 输入格式 输出格式 输入输出样例 说明/提示 题目简化 题目思路 AC代码 题目描述 小 K 同学向小 P 同学发送了一个长度为 8 的 01 字符串来玩数字游戏,小 P 同学想要知道字符串中究竟有多少个 1。 注意:01 字符串为每一个字符是 0 或者 1 的字符串,如“101

洛谷题解 | P1046 陶陶摘苹果

​ 目录 题目描述 输入格式 输出格式 输入输出样例 说明/提示 题目思路 AC代码 题目描述 陶陶家的院子里有一棵苹果树,每到秋天树上就会结出 10 个苹果。苹果成熟的时候,陶陶就会跑去摘苹果。陶陶有个 30 厘米高的板凳,当她不能直接用手摘到苹果的时候,就会踩到板凳上再试试。 现在已知 10 个

洛谷题解 | AT_abc321_c Primes on Interval

目录题目翻译题目描述输入格式输出格式样例 #1样例输入 #1样例输出 #1样例 #2样例输入 #2样例输出 #2样例 #3样例输入 #3样例输出 #3题目简化题目思路AC代码 题目翻译 【题目描述】 你决定用素数定理来做一个调查. 众所周知, 素数又被称为质数,其含义就是除了数字一和本身之外不能被其

Smart - Luogu —— 智能的洛谷

目录安装 Stylus谷歌Edge安装 Smart - Luogu使用尾声 安装 Stylus link 点击推荐下载,获取 crx 文件 谷歌 先点击右上角三个点,再点击扩展程序,然后点击管理扩展程序,进入管理扩展界面,把开发者模式选上,把 crx 文件拖入即可 Edge 先点击右上角三个点,再点

算法学习笔记(3): 倍增与ST算法

倍增 目录倍增查找 洛谷P2249重点变式练习快速幂ST表扩展 - 运算扩展 - 区间变式答案倍增更多的用法优化矩形查询优化建图优化 DP作者有话说 倍增,字面意思即”成倍增长“ 他与二分十分类似,都是基于”2“的划分思想 那么具体是怎么样,我们以一个例子来看 查找 洛谷P2249 依据题面,我们知

算法学习笔记(23): 马尔可夫链中的期望问题

# 马尔可夫链中的期望问题 > 这个问题是我在做 [[ZJOI2013] 抛硬币 - 洛谷](https://www.luogu.com.cn/problem/P3334) 这道题的时候了解的一个概念。 > > 在网上也只找到了一篇相关的内容:[# 马尔可夫链中的期望问题](https://zhua

无所畏惧的求和题解

# 无所畏惧的求和题解 > 本题是本人目前出的题中难度最高的题。 > > 加强版紫色是肯定可以的。 > > 题目链接:[无所畏惧的求和 - 洛谷](https://www.luogu.com.cn/problem/U289496) > > 尽情享受吧! 这道题其实做法有很多: - 待定系数法 + 矩