首页 > 其他分享 >线段树精选题

线段树精选题

时间:2022-10-06 18:24:20浏览次数:78  
标签:选题 ch int 线段 long 树精 区间 开方 root

SP2713 GSS4 - Can you answer these queries IV

题目大意

\(n\)个数,和在\(10^18\)范围内,两种操作:区间开方下取整,查询区间和。

思路

区间开方,其实也是区间修改,只是每个元素修改不一样,还要查询区间和,基本可以确定是线段树。做线段树,我们首先要知道两点(其实也就两点):
①维护什么信息;②如何合并(update)。
既然是查询区间和,肯定是要维护区间和的,难点在于区间开方。
我们从暴力的思路入手,即对于区间的每个元素一个一个的开方,是会T掉的。为什么会T掉呢?因为我们是单点修改,想要加速,就得区间一起修改,但是每个元素的值不一样,不能一起开方,什么时候可以一起开方呢?开方之后值不变,也就是该区间的元素值只能是0或1,这样就可以区间修改了,那我们就看看一个数最坏什么时候变成0或1,\(10^18\)方的话,最多也就30次。
并且和在\(10^18\)范围内,单点修改的情况也就那么多,于是等某个区间全为1或0时,直接返回就可了,反正开方结果也不变,不然就单点开方。
时间复杂度:emm...这个蒟蒻不会算。\(O(能过)\)。

代码

点击查看代码
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>

using namespace std;
inline long long read(){
	long long w=0,f=1;char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-') f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		w=w*10+ch-'0';
		ch=getchar();
	}
	return w*f;
}
struct tree{
	int l;
	int r;
	long long sum;
	bool mark;
}t[400005];
int n,m,tot;
long long a[100005];
void build(int root,int l,int r){
	t[root].l=l,t[root].r=r;
	if(l==r){
		t[root].sum=a[l];
		if(a[l]==1||a[l]==0){
			t[root].mark=1;
		}else{
			t[root].mark=0;
		}
		return;
	}
	int mid=(l+r)/2;
	build(root*2,l,mid);
	build(root*2+1,mid+1,r);
	t[root].sum=t[root*2].sum+t[root*2+1].sum;
	if(t[root*2].mark==1&&t[root*2+1].mark==1){
		t[root].mark=1;
	}else{
		t[root].mark=0;
	}
}

void change(int root,int l,int r){
	if(t[root].l>=l&&t[root].r<=r&&t[root].mark==1){
		return;
	}
	if(t[root].l==t[root].r){
		t[root].sum=sqrt(t[root].sum);
		if(t[root].sum==1||t[root].sum==0){
			t[root].mark=1;
		}else{
			t[root].mark=0;
		}
		return;
	}
	int mid=(t[root].l+t[root].r)/2;
	if(l<=mid){
		change(root*2,l,r);
	}
	if(r>mid){
		change(root*2+1,l,r);
	}
	t[root].sum=t[root*2].sum+t[root*2+1].sum;
	if(t[root*2].mark==1&&t[root*2+1].mark==1){
		t[root].mark=1;
	}else{
		t[root].mark=0;
	}
}
long long ask(int root,int l,int r){
	if(t[root].l>=l&&t[root].r<=r){
		return t[root].sum;
	}
	int mid=(t[root].l+t[root].r)/2;
	long long ans=0;
	if(l<=mid){
		ans+=ask(root*2,l,r);
	}
	if(r>mid){
		ans+=ask(root*2+1,l,r);
	}
	return ans;
}
int main(){
	
    while(scanf("%d",&n)!=EOF){
    	tot++;
	    for(int i=1;i<=n;i++){
		    a[i]=read();
	    }
	    build(1,1,n);
    	printf("Case #%d:\n",tot);
    	m=read();
    	for(int i=1;i<=m;i++){
    		int c,l,r;
    		c=read(),l=read(),r=read();
    		if(l>r){
    			swap(l,r);
    		}
    		if(c==0){
    			change(1,l,r);
    		}else{
    			printf("%lld\n",ask(1,l,r));
    		}
    	}
    }
	return 0;
}

标签:选题,ch,int,线段,long,树精,区间,开方,root
From: https://www.cnblogs.com/Jue-Qing/p/16758139.html

相关文章

  • P3834 【模板】可持久化线段树 2
    P3834主席树模板,求区间第k小。1#include<bits/stdc++.h>2usingnamespacestd;3#definelctr[i].ch[0]4#definerctr[i].ch[1]5#defineLctr[j].ch[0......
  • 动态开点线段树
    引入在普通的线段树中,我们一般要开\(4N\)的数组以避免越界。然而,在一些题目中,空间限制并不允许我们这样做。考虑如下问题:有一个长度为\(n\)的数组,初始全部为\(0\)......
  • 数据结构精选题
    P2391白雪皑皑题目大意给定一个序列,\(m\)次染色,每次将一个区间内的点染色\(i\),染过色的元素可覆盖,求\(m\)次染色后,每个元素的颜色。思路性质:如果暴力染色的话,肯定T飞,......
  • 李超线段树 学习笔记
    Idea主要用于动态维护一个线段或直线集合,支持在平面直角坐标系中插入一条线段或者直线,以及查询某一横坐标上的最值。考虑在x轴上建立一棵线段树,每一个节点\([l,r]\)......
  • 探求|线段或棱上是否存在一个点
    ......
  • [模板]zkw线段树
    讲解在这里[还有一个](https://wenku.baidu.com/view/f27db60ee87101f69e319544.html)A.数列操作单点修改,区间查询code//正青春的年华,就是应该献给直指星辰的梦想啊!......
  • 「CF1455G」Forbidden Value 题解 (DP,线段树合并)
    题目简介已知初始值\(x=0\),给定下面\(2\)种命令:set\(y\)\(v\),令\(x=y\),或花费\(v\)元钱删除该命令;if\(y\)...end,如果\(x==y\),执行if...end中的命令,否则跳......
  • 李超线段树
    李超发明的一种维护平面上有限值域的直线或线段纵坐标大小的一种数据结构。因为是根据线段树研发的,代码也基于线段树,所以叫李超线段树。常规支持:插入一条线段或直线。......
  • 吉司机线段树
    luoguP6242线段树3蒟蒻的代码//线段树3//需要支持的操作://修改:区间加,区间取min,区间求和//查询:区间最大值,区间历史最大值//瓶颈在于区间取min//引入区间最大......
  • 线段树什么的最讨厌了
    发现如果正着从一颗线段树搜到这一个区间,很难搜。所以考虑从一个区间搜出一颗线段树。对于一个区间\([l,r]\),他的父亲区间只可能是\([2*l-r-2,r],[2*l-r-1,r],[l,2*r-l......