校门外的树 加强版 题解

fanzs 2024-01-14 8:45:24 3 返回题目

一、解题思路:

通过题目中要求维护的
移除 中的树(区间修改)
询问 中树的棵数(区间查询)
两个操作,不难看出是一道使用线段树维护的数据结构题。

二、优化:

由于数据范围达到 ,所以显然直接以线段树 的效率是无法通过的。 怎么办呢?

我们发现,在这里我们每个叶子节点的权值只有 ,并且没有区间加操作,这说明当一个区间被修改过后,它的总权值以及内部的任何区间权值均为 ,更形象地说,它 “失去利用价值”,不需要在任何操作和询问中再被修改和查询到了。

于是,我们果断放弃懒标记,而以该区间是否 总权为零作为是否有“利用价值”(势能)的判断方式。此时,无论它被修改区间或是查询区间覆盖多少,它都不会对线段树或是查询结果产生影响。因此,我们成功地通过一个小优化通过了此题。

最后的最后,代码:

#include<bits/stdc++.h>
//#define int long long
using namespace std;
const int N=1e6+10;
int n,m,tree[N<<2];
void pushup(int p){
	tree[p]=tree[p<<1]+tree[p<<1|1];
	return;
}
void build(int p,int l,int r){
	if(l==r){
		tree[p]=1;
		return;
	}
	int mid=(l+r)>>1;
	build(p<<1,l,mid);
	build(p<<1|1,mid+1,r);
	pushup(p);
	return;
}
void modify(int p,int l,int r,int x,int y){
	if(y<l||r<x) return;
	if(tree[p]==0) return;
	if(x<=l&&r<=y){
		tree[p]=0;
		return;
	}
	int mid=(l+r)>>1;
	modify(p<<1,l,mid,x,y);
	modify(p<<1|1,mid+1,r,x,y);
	pushup(p);
	return;
}
int query(int p,int l,int r,int x,int y){
	if(y<l||r<x) return 0;
	if(tree[p]==0) return 0;
	if(x<=l&&r<=y) return tree[p];
	int mid=(l+r)>>1,ans=0;
	ans+=query(p<<1,l,mid,x,y);
	ans+=query(p<<1|1,mid+1,r,x,y);
	return ans;
}
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n>>m;
	build(1,1,n);
	while(m--){
		int op,x,y;
		cin>>op>>x>>y;
		if(op==1) modify(1,1,n,x,y);
		else cout<<query(1,1,n,x,y)<<'\n';
	}
	return 0;
}
{{ vote && vote.total.up }}