首页 > 其他分享 >线段树练习

线段树练习

时间:2023-10-16 18:23:37浏览次数:24  
标签:const int 线段 练习 区间 include mx LL

习题都来自董老师的博客和b站:

Luogu P4198 楼房重建

其实这道题的思路肯定是用线段树,但是为了计算结果线段树需要维护哪些信息?
//mx表示区间内的最大斜率,sum表示区间内可见的,主要就是递归求出sum

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#include<cstdlib>
#define ls u<<1
#define rs u<<1|1
#define mid ((l+r)>>1)
using namespace std;
const int maxn=1e5+10;
const int INF=0x3f3f3f3f;

//其实这道题的思路肯定是用线段树,但是为了计算结果线段树需要维护哪些信息?
//mx表示区间内的最大斜率,sum表示区间内可见的,主要就是递归求出sum

double mx[maxn<<2];
int summ[maxn<<2];
int dfs(int u,int l,int r,double mls){//求右分支sum
	if(mx[u]<=mls) return 0; //剪枝 
	if(l==r) return mx[u]>mls;
	if(mx[ls]<=mls) return dfs(rs,mid+1,r,mls);
	else return dfs(ls,l,mid,mls)+summ[u]-summ[ls];
}
void pushup(int u,int l,int r){ //上传标记   这个过程中要递归更新值 
	mx[u]=max(mx[ls],mx[rs]);
	summ[u]=summ[ls]+dfs(rs,mid+1,r,mx[ls]); //需要查询 
}
void change(int u,int l,int r,int x,double v){ //点修改 
	if(l==r) {
		mx[u]=v;summ[u]=1;return;
	}
	if(x<=mid) change(ls,l,mid,x,v);
	else change(rs,mid+1,r,x,v);
	pushup(u,l,r);
}
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int x,y;cin>>x>>y;
		change(1,1,n,x,(double)y/x);
		cout<<summ[1]<<endl;
	}
	return 0;
} 

  

Luogu P4145 上帝造题的七分钟 2

 

//暴力区间修改,主要是修改的方式不好合并或者打标记
//优化:每个区间维护一个最大值mx,只要mx=1就不用向下分裂了
//每个叶子节点最多修改6次 从12次方到1
//所以综复杂度是6NlogN

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#include<cstdlib>
#define LL long long
#define ls u<<1
#define rs u<<1|1
#define mid ((l+r)>>1)
using namespace std;
const int maxn=1e5+10;
const int INF=0x3f3f3f3f;
//暴力区间修改,主要是修改的方式不好合并或者打标记
//优化:每个区间维护一个最大值mx,只要mx=1就不用向下分裂了
//每个叶子节点最多修改6次 从12次方到1
//所以综复杂度是6NlogN
LL a[maxn];
LL mx[maxn<<2],summ[maxn<<2];
void pushup(int u){
	summ[u]=summ[ls]+summ[rs];
	mx[u]=max(mx[ls],mx[rs]);
}
void build(int u,int l,int r){ //建树
	summ[u]=mx[u]=a[l]; 
 	if(l==r) return;
 	build(ls,l,mid);
 	build(rs,mid+1,r);
 	pushup(u);
}
void change(int u,int l,int r,int x,int y){ //区间修改 
	if(mx[u]==1) return; //剪枝
	if(l==r){
		summ[u]=sqrt(summ[u]);
		mx[u]=sqrt(mx[u]);
		return;
	} 
	if(x<=mid) change(ls,l,mid,x,y);
	if(y>mid) change(rs,mid+1,r,x,y);
	pushup(u);
} 
LL query(int u,int l,int r,int x,int y){ //区间查询 
 	if(x<=l&&r<=y) return summ[u];
	LL s=0;
	if(x<=mid) s+=query(ls,l,mid,x,y);
	if(y>mid) s+=query(rs,mid+1,r,x,y);
	return s; 
}
int main(){
	int n,m,opt,l,r;
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i];
	build(1,1,n);
	cin>>m;
	while(m--){
		cin>>opt>>l>>r;
		if(l>r) swap(l,r);
		if(opt==0){
			change(1,1,n,l,r);
		}
		else cout<<query(1,1,n,l,r)<<endl;
	}
	return 0;
}

  Luogu P1438 无聊的数列

//差分数组可以将点查询--->区间查询 区间修改--->点修改
//把原数组弄成差分数组,方便加上等差数列
//设差分数列为a,等差数列的首项为s,末项为e,公差为d
//区间[l,r]加上等差数量,等于al+s, al+1~ar +d ar+1-e
//!!对原始序列的点查询--转化为对差分序列的区间查询(前缀和)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#include<cstdlib>
#define LL long long
#define ls u<<1
#define rs u<<1|1
#define mid ((l+r)>>1)
using namespace std;
const int maxn=1e5+10;
const int INF=0x3f3f3f3f;
//差分数组可以将点查询--->区间查询     区间修改--->点修改 
//把原数组弄成差分数组,方便加上等差数列
//设差分数列为a,等差数列的首项为s,末项为e,公差为d
//区间[l,r]加上等差数量,等于al+s, al+1~ar +d  ar+1-e
//!!对原始序列的点查询--转化为对差分序列的区间查询(前缀和)
int a[maxn];
LL summ[maxn<<2],tag[maxn<<2]; //懒标记
void pushup(int u){ //上传 
	summ[u]=summ[ls]+summ[rs];
} 
void pushdown(int u,int l,int r){ //下传
	summ[ls]+=tag[u]*(mid-l+1);
	summ[rs]+=tag[u]*(r-mid); //懒标记下传
	tag[ls]+=tag[u];
	tag[rs]+=tag[u];
	tag[u]=0; 
} 
void build(int u,int l,int r){
	summ[u]=a[l];
	tag[u]=0;
	if(l==r) return;
	build(ls,l,mid);
	build(rs,mid+1,r);
	pushup(u);
} 
void change(int u,int l,int r,int x,int y,LL v){
	if(x<=l&&r<=y){
		summ[u]+=(r-l+1)*v;
		tag[u]+=v;
		return;
	}
	pushdown(u,l,r); //先下传
	if(x<=mid) change(ls,l,mid,x,y,v);
	if(y>mid) change(rs,mid+1,r,x,y,v);
	pushup(u); 
}
LL query(int u,int l,int r,int x,int y){
	if(x<=l&&r<=y) return summ[u];
	pushdown(u,l,r);
	LL s=0;
	if(x<=mid) s+=query(ls,l,mid,x,y);
	if(y>mid) s+=query(rs,mid+1,r,x,y);
	return s;
}
int main(){
	int n,m,op,l,r,k,d,p;
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=n;i>1;i--) a[i]-=a[i-1];  //倒着修改
	build(1,1,n) ;//用差分数组建树
	for(int i=1;i<=m;i++){
		cin>>op;
		if(op==1){
			cin>>l>>r>>k>>d;
			change(1,1,n,l,l,k); //首项修改
			if(l+1<=r) change(1,1,n,l+1,r,d) ;//注意要判断范围
			if(r<n) change(1,1,n,r+1,r+1,-(k+d*(r-l))); 
		}
		else {
			cin>>p;
			cout<<query(1,1,n,1,p)<<endl;
		}
	} 
	return 0;
}
 

  Luogu P2184 贪婪大陆

 

//这个和花神那个是一样的,左右括号法
//前缀和思想:把区间查询转化为前缀和之差

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<algorithm>
#include<cstdlib>
#define LL long long
#define ls u<<1
#define rs u<<1|1
#define mid ((l+r)>>1)
using namespace std;
const int maxn=1e5+10;
const int INF=0x3f3f3f3f;
//这个和花神那个是一样的,左右括号法
//前缀和思想:把区间查询转化为前缀和之差
int n,m;
struct node{
	int l,r;
	int sum[2];
}tr[maxn<<2]; 
//sum[0]:区间起点数, sum[1]:区间终点数
void pushup(int u,int k){
	tr[u].sum[k]=tr[ls].sum[k]+tr[rs].sum[k]; //对应的相加 
}
void build(int u,int l,int r){
	tr[u]={l,r,0,0};
	if(l==r) return ;
	build(ls,l,mid);
	build(rs,mid+1,r);
}
void change(int u,int x,int k){
	if(tr[u].l==tr[u].r){
		tr[u].sum[k]++;
		return;
	}
	if(x<=tr[ls].r) change(ls,x,k);
	else change(rs,x,k);
	pushup(u,k);
}
int query(int u,int x,int y,int k){
	if(x>tr[u].r||y<tr[u].l) return 0;
	if(x<=tr[u].l&&tr[u].r<=y) return tr[u].sum[k];
	return query(ls,x,y,k)+query(rs,x,y,k);
}
int main(){
	cin>>n>>m;
	build(1,1,n);
	for(int i=1;i<=m;i++){
		int q,l,r;
		cin>>q>>l>>r;
		if(q==1) change(1,l,0),change(1,r,1);
		else cout<<query(1,1,r,0)-query(1,1,l-1,1)<<endl; //到r的终点数-到l的起点数 
	}
	return 0;
}

  

标签:const,int,线段,练习,区间,include,mx,LL
From: https://www.cnblogs.com/shirlybaby/p/17768043.html

相关文章

  • 线段树模板
    线段树理解起来不难,主要是书写起来比较麻烦这里学的是董晓老师的线段树模板#include<bits/stdc++.h>usingnamespacestd;#definelcp<<1#definercp<<1|1#defineN500005intn,w[N];structnode{intl,r,sum,add;//add用于懒标记}tr[N*4];//建树,深搜递归的过......
  • WINCC 7.5 SP2做web发布练习
    这一片学习笔记我在新浪博客记录过,在这里再次记录一遍,新浪博客地址是WINCC7.5SP2做web发布练习_来自金沙江的小鱼_新浪博客(sina.com.cn)欢迎大家去那边。 有段时间没有弄这个了,最近现场需要用到,这里做一下练习,记录一下。在WINCC7.5SP2做一个项目,运行效果如下打开控制面......
  • WINCC 7.5 SP2 web发布练习-续:不同账户打开不同的页面
    这一篇学习笔记我在新浪博客记录过,地址是WINCC7.5SP2web发布练习-续:不同账户打开不同的页面_来自金沙江的小鱼_新浪博客(sina.com.cn)我在这里再记录一遍在上午练习基础上,WINCC项目程序新增一个页面trend2。在Webnavigator处鼠标右键,选择web浏览发布器,使用向导将新的页面发布......
  • Python 练习实例22
    题目:两个乒乓球队进行比赛,各出三人。甲队为a,b,c三人,乙队为x,y,z三人。已抽签决定比赛名单。有人向队员打听比赛的名单。a说他不和x比,c说他不和x,z比,请编程序找出三队赛手的名单。程序源代码:实例#!/usr/bin/python#-*-coding:UTF-8-*-foriinrange(ord('x'),ord('z')+1):......
  • Python 练习实例23
    题目:打印出如下图案(菱形):*************************程序分析:先把图形分成两部分来看待,前四行一个规律,后三行一个规律,利用双重for循环,第一层控制行,第二层控制列。程序源代码:实例#!/usr/bin/python#-*-coding:UTF-8-*-fromsysimportstdoutforiinrange(4)......
  • *【学习笔记】(7) 线段树及高级用法
    一.普通线段树线段树(SegmentTree)几乎是算法竞赛最常用的数据结构了,它主要用于维护区间信息(要求满足结合律)。与树状数组相比,它可以实现\(O(logn)\)的区间修改,还可以同时支持多种操作(加、乘),更具通用性。接下来我们用这道模板题为例,看看线段树是怎么维护区间和这一信息的。P33......
  • c++ 线段树模板
    洛谷模板:P3372【线段树1】 #include<bits/stdc++.h>#defineintlonglongusingnamespacestd;constintN=1e5+10;inta[N],d[N<<2],b[N<<2];intn,q;inlinevoidbuild(intl,intr,intp){if(l==r){d[p]=a[l];......
  • Python 练习实例21
    题目:猴子吃桃问题:猴子第一天摘下若干个桃子,当即吃了一半,还不瘾,又多吃了一个第二天早上又将剩下的桃子吃掉一半,又多吃了一个。以后每天早上都吃了前一天剩下的一半零一个。到第10天早上想再吃时,见只剩下一个桃子了。求第一天共摘了多少。程序分析:采取逆向思维的方法,从后往前推断。程......
  • 2023/10.14习题练习
    习题:192.168.2.0/24,平均分配给四个部门(四个子网网段)使用,写出各子网网络号、可用主机地址、广播地址以及子网掩码解:读题可知“/24“是这段ip的子网掩码,也就是255.255.255.0,根据子网掩码可以得出该ip的网络位为前24位,所以可以划分的主机位为后8位;本题需要划分4个子网网段,因2^2=4,所......
  • 线段树高阶学习指南
    前置芝士线段树基本框架区间求和constintN=100010;lla[N],st[N*4],f[N*4];intn,q;//向上传voidpushup(llu){st[u]=st[lc]+st[rc];}//向下传voidpushdown(llu,lll,llr,llmid){if(f[u]){st[lc]+=f[u]*(mid-l+1);st[rc]+=f[u]*(r-m......