zoukankan      html  css  js  c++  java
  • [题解] SP2713&P1415 线段树区间每个数开方+区间和

    题目链接:

    SP2713$ $
    P1415

    (1.) 题意人话翻译

    有一个长度为(n)(nleq1e5))的序列(a_n)

    (m)组操作

    每组操作包含三个数:(opt,l,r)

    如果(opt=0),则对区间每个数开方

    如果(opt=1),则输出区间和

    • 注:若(l>r),请交换(l,r)(sum_{i=1}^n a_i leq 10^{18})

    (2.) 条件处理

    题目要求区间每个数开方,这种信息线段树区间修改(lazy)无法维护。

    but 正解就是线段树

    首先关于数据范围(sum_{i=1}^n a_i leq 10^{18}),我们发现最多开六次就变成1了。所以我们对于一个全为1的区间就不必修改。

    其余信息递归到叶子结点修改。

    可是我们要如何维护这个信息呢?

    法一:建两个线段树,一个维护区间和,一个维护区间最大

    如果建立一个维护区间最大的树,我们可以较快维护这个信息。我们只需同时维护两个树上的信息就可以了。

    代码(以(SP2713)为例):

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cmath>
    #include<cstring>
    #include<cctype>
    #include<algorithm>
    #include<stack>
    #include<string>
    #include<queue>
    #include<vector>
    #include<map>
    #include<deque>
    #include<bitset>
    #include<set>
    
    using namespace std;
    
    #define int long long
    #define ull unsigned long long
    #define rg register
    
    namespace MySpace{
        inline int read(){
            rg int s=0,f=0;
            rg char ch=getchar();
    
            while(not isdigit(ch)) f|=(ch=='-'),ch=getchar();
            while(isdigit(ch)) s=(s<<1)+(s<<3)+(ch^48),ch=getchar();
    
            return f?-s:s;
        }
    	
    	const int N=1e5+15;
    	int sumv[N<<2],maxv[N<<2],a[N],n,m;
    	
    	#define ls (now<<1)
    	#define rs (now<<1|1)
    	
    	inline void pushup(int now){
    		sumv[now]=sumv[ls]+sumv[rs];
    		maxv[now]=max(maxv[ls],maxv[rs]);
    	}
    	
    	inline void build(int now,int l,int r){
    		if(l==r){ sumv[now]=maxv[now]=a[l]; return; }
    		rg int mid=(l+r)>>1;
    		build(ls,l,mid);
    		build(rs,mid+1,r);
    		pushup(now);
    	}
    	
    	inline void change(int now,int l,int r,int ql,int qr){
    		if(maxv[now]==1) return;
    		if(l==r){ sumv[now]=maxv[now]=(int)sqrt(sumv[now]); return; }
    		rg int mid=(l+r)>>1;
    		if(ql<=mid) change(ls,l,mid,ql,qr);
    		if(mid<qr) change(rs,mid+1,r,ql,qr);
    		pushup(now);
    	}
    	
    	inline int query(int now,int l,int r,int ql,int qr){
    		if(ql<=l&&r<=qr) return sumv[now];
    		rg int mid=(l+r)>>1,ans=0;
    		if(ql<=mid) ans+=query(ls,l,mid,ql,qr);
    		if(mid<qr) ans+=query(rs,mid+1,r,ql,qr);
    		return ans;
    	}
    	
        inline void main(){
        	rg int cnt=0;
    		while(scanf("%d",&n)!=EOF){
    			printf("Case #%lld:
    ",++cnt);
    			
    			for(rg int i=1;i<=n;i++) a[i]=read();
    			build(1,1,n);
    			m=read();
    			for(rg int i=1;i<=m;i++){
    				rg int opt=read(),l=read(),r=read();
    				if(l>r) swap(l,r);
    				if(opt==0) change(1,1,n,l,r);
    				else printf("%lld
    ",query(1,1,n,l,r));
    			}
    		}
        }
    }
    
    signed main(){
        MySpace::main();
        return 0;
    }
    

    法二:记一个标记代表全为0或一

    我们可以知道全0或1的标记可以用简单的逻辑运算来解决。

    如果两个子区间均为0或1,那么合并起来的区间也为0或1。

    (类似题目:P1087 FBI树题解传送门 题目传送门

    (3.)后记

    后来发现,分块(5)其实就是这个操作。具体思想和上面的法二差不多,也不再赘述。

    题目链接:数列分块入门5

  • 相关阅读:
    01快速入门-03-条件判断和循环
    01快速入门-02-数组和对象
    01快速入门-01-基本语法和数据类型
    《Head First 设计模式》[01] 策略模式
    爬山算法与模拟退火算法的分析与实现
    Java虚拟机(二) —— 运行时数据区的OOM异常
    CoreJava(一)—— Java迭代语句
    Java虚拟机(二) —— 垃圾回收算法与垃圾回收器
    算法学习 —— 使用Linux C++练习OJ
    Java虚拟机(一) —— 运行时数据区
  • 原文地址:https://www.cnblogs.com/UssEnterprise/p/12459395.html
Copyright © 2011-2022 走看看