inf种线段树

楼房重建线段树

模板题

题目传送门
直接是看着题解第一篇学习的,就不给链接了。
显然这道题的题意可以转化为,求从头开始,当前斜率如果大于前面的所有值,则必选,否则不选。
那么,我们考虑,搞一个线段树,每个节点维护两个值,当前区间的最大值,和合法序列长度。
因为是单点修改,所以向下修改的时候是显然的,直接往下递归,但是需要考虑修改过后如何传上去,对于左区间,显然直接继承就好,因为并不会在其前面放上什么东西影响其结果,至于右区间,则需要找到其第一个大于左区间斜率最大值的位置,将其后面的答案加上。
我们考虑对于右区间再次进行递归,每次考虑其左右儿子。
设左区间的最大值为 \(x\),若当前左儿子的最大值大于 \(x\),则显然对于当前左右儿子合并时右区间的贡献是可以直接加上的,然后继续递归左儿子。
否则,我们递归右儿子找到答案即可。
给个代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int n,m;
const int N=1e5+100;
struct node {
	double maxx;
	int len;
}t[N*4];
int read(){int x;cin>>x;return x;}
void write(int x) {
	if(!x) return ;
	write(x/10);
	putchar(x%10+'0');
	return;
} 
void build(int p,int l,int r) {
	t[p].len=0;t[p].maxx=-0x3f3f3f3f;
	if(l==r) return ;
	int mid=(l+r)>>1;
	build(p*2,l,mid);
	build(p*2+1,mid+1,r);
}
void push_up(int x,int p,int l,int r) {
	// cerr<<l<<" "<<r<<" "<<x<<" "<<p<<" "<<t[p].maxx<<" "<<t[x].maxx<<endl;
	if(l==r) {
		// cerr<<l<<" "<<p<<" "<<x<<" "<<t[p].maxx<<" "<<t[x].maxx<<endl;
		if(t[p].maxx<t[x].maxx) t[p].len++;
		return ; 
	}
	int mid=(l+r)>>1;
	if(t[x*2].maxx<=t[p].maxx) push_up(x*2+1,p,mid+1,r);
	else {
		t[p].len=t[p].len+t[x].len-t[x*2].len;
		push_up(x*2,p,l,mid);
	}
}
void change(int p,int l,int r,int tl,double x) {
	if(l==r) {
		t[p].maxx=x;
		t[p].len=1;
		// cerr<<p<<" "<<l<<" "<<r<<" "<<x<<endl;
		return ;
	}
	int mid=(l+r)>>1;
	if(tl<=mid) change(p*2,l,mid,tl,x);
	else change(p*2+1,mid+1,r,tl,x);
	t[p]=t[p*2];
	// cerr<<tl<<" "<<p<<" "<<t[p*2].maxx<<endl;
	push_up(p*2+1,p,mid+1,r);
	t[p].maxx=max(t[p*2].maxx,t[p*2+1].maxx);
}
int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	n=read();m=read();
	int x,y;
	for(int i=1;i<=m;i++) {
		x=read();y=read();
		change(1,1,n,x,y*1.0/x);
		cout<<t[1].len<<"\n";
	}
	return 0;
}

假装自己写完了。溜了溜了

线段树历史和

模板

题目传送门
题解传送门
学习中……
好像理解为啥一定要用矩阵乘法维护了,因为好写。
模板题我认为不需要用矩阵乘法维护好像是因为那题求得实际上并非线段树历史和,而是线段树历史最值,这东西直接一个懒标记就做完了。
但是我们仍然可以通过这道题对线段树历史和有一个初步的了解。
好像说,

posted @ 2025-08-10 15:30  wjx_2010  阅读(5)  评论(0)    收藏  举报