ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

上帝造题的七分钟2 / 花神游历各国

2019-10-05 22:08:28  阅读:279  来源: 互联网

标签:10 ch int sqrt 花神 七分钟 区间 造题 开方


题目描述

"第一分钟,X说,要有数列,于是便给定了一个正整数数列。

第二分钟,L说,要能修改,于是便有了对一段数中每个数都开平方(下取整)的操作。

第三分钟,k说,要能查询,于是便有了求一段数的和的操作。

第四分钟,彩虹喵说,要是noip难度,于是便有了数据范围。

第五分钟,诗人说,要有韵律,于是便有了时间限制和内存限制。

第六分钟,和雪说,要省点事,于是便有了保证运算过程中及最终结果均不超过64位有符号整数类型的表示范围的限制。

第七分钟,这道题终于造完了,然而,造题的神牛们再也不想写这道题的程序了。"

——《上帝造题的七分钟·第二部》

所以这个神圣的任务就交给你了。

输入格式

第一行一个整数nn,代表数列中数的个数。

第二行nn个正整数,表示初始状态下数列中的数。

第三行一个整数mm,表示有mm次操作。

接下来mm行每行三个整数k,l,r

  • k=0表示给[l,r][l,r]中的每个数开平方(下取整)
  • k=1表示询问[l,r][l,r]中各个数的和。

数据中有可能l>rl>r,所以遇到这种情况请交换l和r。

输出格式

对于询问操作,每行输出一个回答。

输入输出样例

输入

10
1 2 3 4 5 6 7 8 9 10
5
0 1 10
1 1 10
1 1 5
0 5 8
1 4 8

 

输出

19
7
6

说明/提示

对于30%的数据,1≤n,m≤10001≤n,m≤1000,数列中的数不超过32767。

对于100%的数据,1 ≤ n,m ≤ 100000,1 ≤ l,r ≤ n1≤l,r≤n,数列中的数大于0,且不超过1012

注意l有可能大于r,遇到这种情况请交换l,r。

 


 

一个序列,支持区间开方与求和操作。

算法:线段树实现开方修改与区间求和

分析:

  • 显然,这道题的求和操作可以用线段树来维护
  • 但是如何来实现区间开方呢
  • 大家有没有这样的经历:玩计算器的时候,把一个数疯狂的按开方,最后总会变成 11,之后在怎样开方也是 11 (\sqrt1=11​=1)
  • 同样的,\sqrt0=00​=0
  • 所以,只要一段区间里的所有数全都 \leq 1≤1 了,便可以不去修改它

实现:

  • 线段树维护区间和 sumsum 与最大值 MaxMax
  • 在修改过程中,只去修改 Max > 1Max>1 的区间
  • 到了叶子节点对sumsum和MaxMax进行开方就行了

复杂度:

  • 每个数 \leq 10 ^ {12}≤1012,所以至多开方66次便可以得到11
  • 每次操作是 \log nlogn的,总复杂度O(n \log n)O(nlogn)

注意事项:

  • 请使用long long
  • 可能 l > rl>r

 


 

 

由于sqrt(a)+sqrt(b)!=sqrt(a+b)

所以用不了Lazy。至少我是这么觉得的

那怎么办呢?

那就只能暴力啊,把区间内每一个叶节点找到(l==r),开方

但是

开方有一个小小的问题:

sqrt(1)=1,sqrt(0)=0。玩过计算器的人都知道

题目数据上限为10的12次方

所以对于这个范围内的数,最多开方6次(取下整),都会变为1。计算器摁出来的

So 我们需要一个数组mx,保存这个区间内的最大值,同样用线段树维护

当一个mx[k]<=1时(也就是整个区间都是0或1时,继续修改没有意义),直接return,

查询还是普通的区间查询。

k<<1 就是2*k (位运算左移)

k<<1|1 就是2k+1 (因为k2必然为偶数,二进制末位为0,或1以后,0变为1,也就是加1)


 

  • 通过观察可以得到,在此题数据范围内一个数开方 6 次就一定会变成 1 ,我们就没必要再对这个数开方了,可以利用并查集直接跳过.

  • 我们用并查集维护这样的东西:

    对于第 i 个数 a[ i ],当 a[ i ] 不等于 1 时,他的祖先是他自己,即 f[ i ] = i,当 a[ i ] = 1 时,f[ i ] = 下一个不等于 1 的数的位置 j (i < j <=n+1),这里要注意 f[ n + 1 ] = n + 1.

    然后我们在 l ~ r 区间内寻找祖先是自己的数修改即可,具体可用指针不断更新,find 找祖先.

  • 然后我们用树状数组维护即可.

三. 代码

加了个快读没开 O2 最慢点 45ms, 开 O2 最慢点29ms,代码去掉空行后40来行,也是比较短了.

 



#include <bits/stdc++.h> #define int long long #define ls k<<1 #define rs k<<1|1 using namespace std; const int N=1000005; int n,m; int a[N],maxn[N<<2],sum[N<<2];//4倍空间 inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch<'0'||ch> '9') if(ch=='-') f=-1,ch=getchar();//ch=='-'?q=1:0,ch=getchar(); while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(); return x * f; } inline void up(int k){ maxn[k]=max(maxn[ls],maxn[rs]); sum[k]=sum[ls]+sum[rs]; } inline void build(int k,int l,int r){ if(l==r){ sum[k]=maxn[k]=a[l]; return; } int mid=(l+r)>>1; build(ls,l,mid); build(rs,mid+1,r); up(k); } inline void change(int k,int l,int r,int L,int R){ if(l==r && l>=L && r<=R){ sum[k]=maxn[k]=sqrt(sum[k]); return ; } int mid=(l+r)>>1; if(L<=mid && maxn[ls]>1) change(ls,l,mid,L,R); if(mid<R && maxn[rs]>1) change(rs,mid+1,r,L,R); up(k); } inline int query(int k,int l,int r,int L,int R){ if(L<=l && r<=R) return sum[k]; int mid=(l+r)>>1; int ans=0; if(L<=mid) ans+=query(ls,l,mid,L,R); if(mid<R) ans+=query(rs,mid+1,r,L,R); return ans; } signed main(){ n=read(); memset(sum,0,sizeof sum); memset(maxn,0,sizeof maxn); for(int i=1;i<=n;i++) a[i]=read(); build(1,1,n); m=read(); while(m--){ int op=read(),l=read(),r=read(); if(l>r) swap(l,r); if(op==0) change(1,1,n,l,r); else printf("%d\n",query(1,1,n,l,r)); } printf("\n"); }

 

标签:10,ch,int,sqrt,花神,七分钟,区间,造题,开方
来源: https://www.cnblogs.com/aprincess/p/11625890.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有