线段树笔记草稿

一个左节点u << 1和右节点u << 1 | 1 的证明
请添加图片描述

区间修改部分

1.批量等值修改

前提条件

是要区间修改,区间查询,且修改操作修改的值是相同的

情景

一般是要对一个数组执行k次操作,每次改变其中一个区间内所有元素的值,然后询问一个区间内所有元素的最值或总和,

题解代码
void Pushdown(int k){    //更新子树的lazy值,这里是RMQ的函数,要实现区间和等则需要修改函数内容
    if(lazy[k]){    //如果有lazy标记
        lazy[k<<1] += lazy[k];    //更新左子树的lazy值
        lazy[k<<1|1] += lazy[k];    //更新右子树的lazy值
        t[k<<1] += lazy[k];        //左子树的最值加上lazy值
        t[k<<1|1] += lazy[k];    //右子树的最值加上lazy值
        lazy[k] = 0;    //lazy值归0
    }
}

注意懒标记中储存区间修改的值与长度的乘积,大概率开long long

struct node {
    int l, r;
    ll val;
    ll lazy;
}t[N << 2];
void pushdown(node& op, ll lazy) {
    op.val += lazy * (op.r - op.l + 1);
    op.lazy += lazy;
}
void pushdown(int x) {
    if (!t[x].lazy) return;
    pushdown(t[x << 1], t[x].lazy), pushdown(t[x << 1 | 1], t[x].lazy);
    t[x].lazy = 0;
}
void build(int l, int r, int x = 1\没有值传入时,默认初始化为1) {
    t[x] = { l, r, w[l], 0 };
    if (l == r) return;
    int mid = l + r >> 1;
    build(l, mid, x << 1), build(mid + 1, r, x << 1 | 1);
    pushup(x);
}

void modify(int l, int r, int c, int x = 1) {
    if (l <= tr[x].l && r >= tr[x].r) { pushdown(tr[x], c); return; }\通过打标记的方法来赋值
    pushdown(x);
    操作时遇到了懒标记就处理下(懒的思想,顺路就搞下,不顺路就拖着不干)
    int mid = tr[x].l + tr[x].r >> 1;
    if (l <= mid) modify(l, r, c, x << 1);\modify的递归也变成了和线段树单点修改query里的递归形式,有交集就递归。
    if (r > mid) modify(l, r, c, x << 1 | 1);
    pushup(x);
}

ll ask(int l, int r, int x = 1) {
    if (l <= t[x].l && r >= tr[x].r) return tr[x].val;
    pushdown(x);
    //query的唯一变化就是加上了一个pushdown();
    int mid = tr[x].l + tr[x].r >> 1;
    ll res = 0;
    if (l <= mid) res += ask(l, r, x << 1);
    if (r > mid) res += ask(l, r, x << 1 | 1);
    return res;
}

int main()
{
    int n, m; cin >> n >> m;
    rep(i, n) scanf("%d", &w[i]);
    build(1, n);
    
    while (m--) {
        char op[2]; int l, r; scanf("%s %d %d", op, &l, &r);
        
        if (*op == 'Q') printf("%lld\n", ask(l, r));
        else {
            int c; scanf("%d", &c);
            modify(l, r, c);
        }
    }
    return 0;
}


自己写的acwing式代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 1e5 + 10;
typedef long long ll;
struct node{
	int l,r;
	ll sum;
	ll lazy;
}tr[N<<2];
int w[N];

void pushdown(node &x,ll lazy){
	x.sum += lazy*(x.r - x.l + 1);
	x.lazy += lazy;
}
void pushdown(int u){
	if(!tr[u].lazy) return;
	pushdown(tr[u<<1],tr[u].lazy),pushdown(tr[u<<1|1],tr[u].lazy);
	tr[u].lazy = 0;
}
void pushup(int u){
	tr[u].sum = tr[u<<1].sum + tr[u<<1|1].sum;
}
void build(int u,int l,int r){
	tr[u].l = l,tr[u].r = r,tr[u].sum =w[r];
	if(l == r) return;
	int mid = l + r >> 1;
	build(u<<1,l,mid),build(u<<1|1,mid+1,r);
	pushup(u);
}
ll query(int u,int l,int r){
	if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
	ll res = 0;
	int mid = tr[u].l + tr[u].r >> 1;
	pushdown(u);
	if(l <= mid) res += query(u<<1,l,r);
	if(r > mid) res += query(u<<1|1,l,r);
	return res;
}
void modify(int u,int l,int r,int v){
	if(tr[u].l >= l && tr[u].r <= r){
		pushdown(tr[u],v);
		return;
	}
	pushdown(u);
	int mid = tr[u].l + tr[u].r >> 1;
	if(l <= mid) modify(u<<1,l,r,v);
	if(r > mid) modify(u<<1|1,l,r,v); 
	pushup(u);
}
int main(){
	int n,m;
	cin >> n >> m;
	for(int i =1;i <= n;i++) scanf("%d",&w[i]);
	build(1,1,n);
	while(m--){
		char op[2];
		int l,r;
		scanf("%s%d%d",op,&l,&r);
		if(*op == 'Q') printf("%lld\n",query(1,l,r));
		else{
			int c;
			scanf("%d",&c);
			modify(1,l,r,c);
		}
	}
	return 0;
}




注意:

线段树的初始化在build里完成,多组数据集时不需要再额外初始化。


2.批量自适应修改

前提条件

是区间修改,区间查询,且修改操作的修改的值是根据具体节点储存的值而变化的,比如开根,幂,替换,乘除;


情景

对一个序列里的元素执行k次自适应操作,每次操作一个区间,然后询问区间内所有元素的值。
也有询问某个区间内所有值经过某种处理后的值。(此种问法是询问时用一个变量储存找到的值,经过处理后返回



例题1单种操作

Can you answer these queries?
22ACM集训队-树状数组与线段树基础 - Virtual Judge (vjudge.net)
主要就是把modify的递归条件改成了和传统query操作相同的有交集
复杂度比较高,可能需要一些剪枝,某条件下操作了等于白操作之类的。

代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 1e5 + 10;
struct node{
	int l,r;
	ll sum;
}tr[N<<2];
ll w[N];
void pushup(int u){
	tr[u].sum = tr[u<<1].sum + tr[u<<1|1].sum;
}
void build(int u,int l,int r){
	tr[u] = {l,r,w[r]};
//	cout << w[r] << endl;
	if(l == r) return;
	int mid = l + r >> 1;
	build(u<<1,l,mid),build(u<<1|1,mid+1,r);
	pushup(u);
}
ll query(int u,int l,int r){
	if(tr[u].l >= l && tr[u].r <= r) {
		return tr[u].sum;
		
	}	
//		cout << tr[u].sum;
	ll res =0 ;
	int mid = tr[u].l + tr[u].r >> 1;
	if(l <= mid) res += query(u<<1,l,r);
	if(r > mid) res += query(u<<1|1,l,r);
	
	return res;
}
void modify(int u,int l,int r){
	
	if(tr[u].l == tr[u].r) tr[u].sum = sqrt(tr[u].sum);
	else{
		if(tr[u].l >= l && tr[u].r <= r && tr[u].sum == tr[u].r - tr[u].l + 1) return;
		int mid = tr[u].l + tr[u].r >> 1;
		if(l <= mid) modify(u<<1,l,r);
		if(r > mid) modify(u<<1|1,l,r);
		pushup(u);	
	}
	
}
	

int main()
{
    int T = 1;
    int n, m;
    while (cin >> n) {
        for(int i = 1;i <= n;i++) scanf("%lld", &w[i]);
        
        build(1,1, n);
        
        printf("Case #%d:\n", T++);
        scanf("%d", &m);
        while (m--) {
            int op, l, r; scanf("%d %d %d", &op, &l, &r);
            if (l > r) swap(l, r);
//            cout << l << " " << r << endl;
            if (op) printf("%lld\n", query(1,l, r));
            else modify(1,l, r);
        }
        printf("\n");
    }
    return 0;
}

例题2多种操作

Transformation HDU - 4578
22ACM集训队-树状数组与线段树基础 - Virtual Judge (vjudge.net)

题解代码

Transformation HDU - 4578 (线段树,审题很重要)_Soar-的博客-CSDN博客

#include<bits/stdc++.h>
 
using namespace std;
#define lson i<<1,l,m
#define rson i<<1|1, m+1,r
const int mod = 10007;
const int maxn=1e5+10;
 
int x[maxn<<2],flag[maxn<<2];
 x是tr,flag是lazy
void pushup(int i,int l,int r)
{
    if(!flag[i<<1] || !flag[i<<1|1])左右子节点无值
        flag[i] = 0;
    else if(x[i<<1] != x[i<<1|1])左右子节点有值且不等
        flag[i] = 0;
    else flag[i]=1,x[i]=x[i<<1];左右子节点值相等
    所以这是一个记录懒标记的函数,如果左右子节点的值相同,就上传。
    
    通过用父节点的节点的值来代表子节点的值接受处理,降低复杂度
}
 
void pushdown(int i,int l,int r)
{
    if(flag[i])
    {
        flag[i<<1] = flag[i<<1|1] =1;
        x[i<<1] = x[i<<1|1] = x[i];
        flag[i]=0;
    }
    这是一个下传懒标记并处理懒标记的函数,如果有懒标记,说明这个节点是代表子节点接受处理的,所以直接将值下传到子节点,然后清除懒标记
}
 
void update(int ql,int qr,int p,int v,int i,int l,int r)
{
	妙:直接传入op,也就是p,根据p的值进行不同操作,减少了很多赘余的代码。
	我写时想的是写3个modify,也就是update,根据op不同,调用不同的modify,麻烦得很。
    if(ql<=l && qr>=r && flag[i])
    这里是有懒标记,且节点区间全都在需要处理的区间内,直接处理当前节点,然后pushdown,就可以实现区间处理
    {
        if(p==1)
            x[i] = (x[i]+v)%mod;
        else if(p==2)
            x[i] = (x[i]*v)%mod;
        else x[i] = v;
        修改当前节点值的话是不需要pushup的,因为pushup的操作是根据子节点的值来决定是否赋予当前节点一个懒标记,只修改当前节点值,代表当前节点已经是叶子节点,或者左右节点值相同,所以就算pushup了,懒标记还是会保持原有状态
        return;
    }
    pushdown(i,l,r);
 可能没有懒标记,会需要逐个单点修改,所以用两个if的原始query递归形式
    int m = (l+r)>>1;
    if(ql<=m) update(ql,qr,p,v,lson);
    if(qr>m) update(ql,qr,p,v,rson);
    进行子节点单点值修改操作后都需要pushup,来更新懒标记状态
    
    pushup(i,l,r);
}
int query(int ql,int qr,int num,int i,int l,int r)
l,r是当前节点的l,r
{
    if(flag[i] && ql<=l && qr>=r)
    {
        int ans=1;
        for(int j=0;j<num;j++)ans=(ans*x[i])%mod;//pow操作,每次pow取余,如果是10007的三次方就有可能爆int了,所以用循环来每次操作后取余
        ans=(ans*(r-l+1))%mod;
        return ans;
    }
 
    pushdown(i,l,r);
 
    int m = (l+r)>>1;
    int ans=0;
    if(ql<=m)ans+=query(ql,qr,num,lson);
    if(qr>m)ans+=query(ql,qr,num,rson);
    return ans%mod;
}
 
int main()
{
    int n,m;
    while(cin>>n>>m,n||m)
    {
        memset(flag,1,sizeof flag);
        memset(x,0,sizeof x);
        int p,x,y,v;
        while(m--)
        {
            scanf("%d%d%d%d",&p,&x,&y,&v);
            if(p>=1 && p<=3)update(x,y,p,v,1,1,n);
            else printf("%d\n",query(x,y,v,1,1,n));
        }
    }
}

经过模仿后得到的acwing版代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 1e5 + 10,mod = 10007;
struct node{
	int l,r;
	int sum;
	int lazy;
}tr[N<<2];
void pushup(int u){
	if(!tr[u<<1].lazy || !tr[u<<1|1].lazy) tr[u].lazy = 0;
	有一个子节点懒标记是0(当前节点的子节点的两个子节点的值不相等)则当前节点懒标记就变成0,由此可以推断出,懒标记的含义是表示当前节点的子树里所有节点的值 ,都相等,可以直接用当前节点的值来进行操作。 

	else if(tr[u<<1].sum != tr[u<<1|1].sum) tr[u].lazy = 0;
	else tr[u].lazy = 1,tr[u].sum = tr[u<<1].sum;
}
void pushdown(int u){
	if(tr[u].lazy){
		tr[u<<1].lazy = tr[u<<1|1].lazy = 1;
		tr[u<<1].sum = tr[u<<1|1].sum = tr[u].sum;
		tr[u].lazy = 0;
	}
}
void build(int u,int l,int r){
	tr[u] = {l,r,0,1};
	if(l == r) return;
	int mid = l + r >> 1;
	build(u<<1,l,mid),build(u<<1|1,mid+1,r);
}
void modify(int u,int l,int r,int op,int v){
	if(tr[u].l >= l && tr[u].r <= r && tr[u].lazy){
		if(op == 1) tr[u].sum = (tr[u].sum + v) % mod;
		else if(op == 2) tr[u].sum = (tr[u].sum * v)%mod;
		else {
			tr[u].sum = v;	
		}		

		return;
	}
	pushdown(u);
	有懒标记要先处理,然后再运算。
	int mid = tr[u].l + tr[u].r >> 1;
	if(l <= mid) modify(u<<1,l,r,op,v);
	if(r > mid) modify(u<<1|1,l,r,op,v);
	pushup(u);
	
}
int query(int u,int l,int r,int v){
	if(tr[u].l  >= l && tr[u].r <= r && tr[u].lazy){
		int res = 1;
		for(int i = 0;i < v;i++) res = (res * tr[u].sum) % mod;
		res = res * (tr[u].r - tr[u].l + 1) % mod;
		return res;
	}
	pushdown(u);
	int mid = tr[u].l + tr[u].r >> 1;
	int res = 0;
	if(l <= mid) res = (res + query(u<<1,l,r,v) )	%mod; 
	if(r > mid) res = (res + query(u<<1|1,l,r,v)) % mod;
	return res % mod;
}
int main(){
	int n,m;
	while(cin >> n >> m,n||m){
//		for(int i=0;i <= N << 2;i++) tr[i]= {0,0,0,0};	
		build(1,1,n);
		int op,l,r,v;
		while(m--){
			scanf("%d%d%d%d",&op,&l,&r,&v);
//			cout << op << " " << l << " " << r << " " << v << endl;
			if(op >=1 && op <= 3){
				modify(1,l,r,op,v);
			}
			else {
				printf("%d\n",query(1,l,r,v));
			}
		}
	}
	return  0;
}

注意

注意点就是非数组模拟节点的代码要用build初始化,然后懒标记初始化为1,因为代表的含义是两个子节点值是否相等

其他套路:

涉及到一个多组输入的套路
前提条件是没有明确组数,结束关键词的多组数据集输入
取反while(~scanf(“%d”,&n)
和while(scanf(“%d”,&n) != EOF)
还有while(cin >> n)三种形式

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/10649.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ChatGPT文本框再次升级,打造出新型操作系统

在ChatGPT到来之前&#xff0c;没有谁能够预见。但是&#xff0c;它最终还是来了&#xff0c;并引起了不小的轰动&#xff0c;甚至有可能颠覆整个行业。 从某种程度上说&#xff0c;ChatGPT可能是历史上增长最快的应用程序&#xff0c;仅在两个多月就拥有了1亿多活跃用户&…

Adaptive Weight Assignment Scheme For Multi-task Learning

Adaptive Weight Assignment Scheme For Multi-task Learning 题目Adaptive Weight Assignment Scheme For Multi-task Learning译题用于多任务学习的自适应权重分配方案时间2022年期刊/会议IAES International Journal of Artificial Intelligence (IJ-AI) 摘要&#xff1a;如…

【AutoGPT】你自己运行,我先睡了—— ChatGPT过时了吗?

系列文章目录 【AI绘画】Midjourney和Stable Diffusion教程_山楂山楂丸的博客-CSDN博客 目录 系列文章目录 前言 一、AutoGPT是什么&#xff1f; 二、AutoGPT带来的利弊 三、AutoGPT和ChatGPT区别 四、未来 总结 前言 ChatGPT是否过时&#xff1f;AutoGPT的兴起&#…

MappingGenerator PRO 2023.3 Visual Studio 2019-2022

您的私人编码助手 MappingGenerator 最初是作为 AutoMapper 的设计时替代品创建的。现在它正在演变为编码助手&#xff0c;您可以将最平凡的编码任务委派给它&#xff1a; 生成映射生成显式转换实施克隆生成投影表达式脚手架方法调用脚手架对象创建清理方法调用方便ILogger的使…

ChatGPT风口下的中外“狂飙”,一文看懂微软、谷歌、百度、腾讯、华为、字节跳动们在做什么?

毫无疑问&#xff0c;ChatGPT正成为搅动市场情绪的buzzword。 历史经历过无线电&#xff0c;半导体&#xff0c;计算机&#xff0c;移动通讯&#xff0c;互联网&#xff0c;移动互联网&#xff0c;社交媒体&#xff0c;云计算等多个时代&#xff0c;产业界也一直在寻找Next Bi…

Golang每日一练(leetDay0031)

目录 91. 解码方法 Decode Ways &#x1f31f;&#x1f31f; 92. 反转链表 II Reverse Linked List II &#x1f31f;&#x1f31f; 93. 复原 IP 地址 Restore IP Addresses &#x1f31f;&#x1f31f; &#x1f31f; 每日一练刷题专栏 &#x1f31f; Golang每日一练…

【JVM】JVM之执行引擎

文章目录一、前言二、名词解释机器码指令指令集汇编语言高级语言字节码虚拟机&物理机前端编译器&后端编译器三、JVM之执行引擎执行引擎是如何工作的&#xff1f;解释器即时编译器&#xff08;JIT&#xff09;分层编译策略虚拟机执行模式热点代码&探测方式1&#xf…

如何在 Linux 中使用 Chage 命令,修改Linux系统用户密码更改策略

Chage是一个用于修改Linux系统用户密码更改策略的命令行工具。在本文中&#xff0c;我们将介绍如何在Linux系统中使用Chage命令。 检查用户密码过期信息 使用Chage命令可以检查用户密码更改策略和过期信息。要检查特定用户的密码过期信息&#xff0c;可以使用以下命令&#x…

PPT NO.1【用ppt如何做一张海报+字体】

PPT做得好的人&#xff0c;一定是站在观众的角度思考的人。 1、设置幻灯片尺寸大小&#xff1a; 设置完成后如下&#xff1a; 2、加载一张自己喜欢的图片进来&#xff1a;【图片越高清越好】 将图片铺满空白的地方&#xff0c;调整好自己喜欢的区域&#xff1a; 做裁剪&#xf…

数据结构---递归转化为非递归

递归转化为非递归前言快速排序非递归归并排序的非递归前言 为什么要学习非递归写法呢&#xff1f; 当我们在用递归实现一个程序的时候&#xff0c;要考虑一个问题&#xff0c;这个程序用递归去实现&#xff0c;当数据量庞大的时候&#xff0c;会不会造成栈溢出(STACK OVERFLOW…

代码随想录_226翻转二叉树、101对称二叉树

leetcode 226. 翻转二叉树 ​​​226. 翻转二叉树 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1]示例 2&#xff1a; 输入&#xff1a;r…

算法训练第五十五天 | 392.判断子序列、115.不同的子序列

动态规划part15392.判断子序列题目描述思路总结115.不同的子序列题目描述思路392.判断子序列 题目链接&#xff1a;392.判断子序列 参考&#xff1a;https://programmercarl.com/0392.%E5%88%A4%E6%96%AD%E5%AD%90%E5%BA%8F%E5%88%97.html 题目描述 给定字符串 s 和 t &…

【CocosCreator入门】CocosCreator组件 | Graphics(绘制)组件

Cocos Creator 是一款流行的游戏开发引擎&#xff0c;具有丰富的组件和工具&#xff0c;其中Graphics组件允许您在游戏中绘制2D图形和几何形状&#xff0c;并通过编写脚本来控制其外观和行为。 目录 一、组件属性 二、组件方法 三、脚本示例 一、组件属性 属性功能说明lineW…

面试篇-Java并发之CAS:掌握原理、优缺点和应用场景分析,避免竞态问题

1、CAS介绍及原理 多线程中的CAS&#xff08;Compare-and-Swap&#xff09;操作是一种常见的并发控制方法&#xff0c;用于实现原子性更新共享变量的值。其核心思想是通过比较内存地址上的值和期望值是否相等来确定是否可以进行更新操作&#xff0c;从而避免多线程条件下的竞态…

用PyTorch构建基于卷积神经网络的手写数字识别模型

本文参加新星计划人工智能(Pytorch)赛道&#xff1a;https://bbs.csdn.net/topics/613989052 目录 一、MINST数据集介绍与分析 二、卷积神经网络 三、基于卷积神经网络的手写数字识别 一、MINST数据集介绍与分析 MINST数据库是机器学习领域非常经典的一个数据集&#xff0c…

动力节点王鹤SpringBoot3笔记——第八章 文章管理模块

目录 第八章 文章管理模块 8.1 配置文件 8.2 视图文件 8.3 Java代码 第八章 文章管理模块 创建新的Spring Boot项目&#xff0c;综合运用视频中的知识点&#xff0c;做一个文章管理的后台应用。 新的Spring Boot项目Lession20-BlogAdmin。Maven构建工具&#xff0c;包…

【UE4】关卡流送的demo

关卡流送功能可以将地图文件加载到内存中&#xff0c;或者从内存中卸载&#xff0c;并在游戏过程中切换地图的可视性。 这样一来&#xff0c;场景便能拆分为较小的地图块&#xff0c;并且只有相关部分才会占用资源并被渲染。 正确设置后&#xff0c;开发者便能创建大型、无缝衔…

开源流媒体服务器ZLMediaKit在Windows上运行、配置、按需拉流拉取摄像头rtsp视频流)并使用http-flv网页播放

场景 目前市面上有很多开源的流媒体服务器解决方案&#xff0c;常见的有SRS、EasyDarwin、ZLMediaKit和Monibuca等。 1、SRS GitHub - ossrs/srs: SRS is a simple, high efficiency and realtime video server, supports RTMP, WebRTC, HLS, HTTP-FLV, SRT, MPEG-DASH and …

ChatGPT批量翻译-ChatGPT批量生成多国语言

ChatGPT翻译的准吗 ChatGPT是一种基于Transformer架构的自然语言处理技术&#xff0c;其翻译准确性取决于所训练的模型和数据集的质量。在特定的语料库和训练数据下&#xff0c;ChatGPT可以实现一定程度的准确翻译。但是&#xff0c;与人工翻译相比&#xff0c;ChatGPT的翻译质…

LeetCode_101

千奇百怪的排序算法 快速排序 采用左闭右开的二分写法 归并排序 插入排序 冒泡排序 选择排序 以上代码的调用方式&#xff1a; 快速选择 在一个未排序的数组中&#xff0c;找到第 k 大的数字 快速选择一般用于求解 k-th Element 问题&#xff0c;可以在 O(n) 时间复杂度&…