我用13行摆烂了你的oj4
13行代码AC oj4是怎么回事呢?13行代码AC oj4相信大家都很熟悉,但是13行代码AC oj4是怎么回事呢,下面就让小编带大家一起了解吧。
13行代码AC oj4,其实就是13行代码AC oj4,大家可能会很惊讶13行代码AC oj4怎么会13行代码AC oj4呢?但事实就是这样,小编也感到非常惊讶。
这就是关于13行代码AC oj4的事情了,大家有什么想法呢,欢迎在评论区告诉小编一起讨论哦!
清华大学电子工程系《数据与算法》课程在线评测系统作业第四题:上班摸鱼 算账的会计
(此博客会随着作者的理解加深而逐步修改)
闲话少说,先奉上代码:
#include<cstdio>
int a[200006],t[200006],n;
void add(int x,int k){for(;x<=n;x+=x&-x){t[x]+=k;}}
int sum(int x){int sum=0;for(;x;x-=x&-x)sum+=t[x];return sum;}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){scanf("%d",&a[i]);add(i,a[i]);}
for(int i=1;i<=2*n;i++){
int boo;scanf("%d",&boo);
if(boo==1){int x,k;scanf("%d%d",&x,&k);k-=sum(x)-sum(x-1);add(x,k);}
else{int x,y;scanf("%d%d",&x,&y);printf("%d\n",sum(y)-sum(x-1));}
}
}
这是一道典型的树状数组模板题。虽然oj提示要求用线段树解决,线段树的题解我会另写一篇博客发出来
原题:
在采用快读之前获得了180ms的坏成绩
首先声明,本人并无oi经验,只是树状数组初学者,正如著名acmer所说:“很多初学者肯定和我一样,只知晓 BIT 代码精炼,语法简明。对于原理好像了解,却又如雾里探花总感觉隔着些什么。”
本人仅在此对与本题有关的树状数组做简要说明,请感兴趣的同学自行学习。
推荐阅读:树状数组(BIT)—— 一篇就够了 - Last_Whisper - 博客园 (cnblogs.com)(哇我和大佬用了同样的博客园主题耶)
用蛮力法完成区间修改与查询的复杂度分析
在学习数据结构之前,在那个不用担心tle的《程序设计基础》年代,我们大家对这道题自然是随手推闭眼写的,即建立一个数组存储各个值,然后用循环暴力方法修改和查询。假设数组长度为n,操作区间长度为m,操作数为k,那么完成这样一套操作的时间复杂度为O(k(n+m)),这自然是题目设计合理,样例难度递进,练习范围全面,提升效果最好的无系数算oj所不能容忍的。我们发现,k次操作的复杂度几乎是不可降低的,对于每一次操作,我们有必要将线性复杂度进一步优化。
树状数组的定义
顾名思义,树状数组是用数组模拟了树的功能。学过线段树的同学可能知道,对于一个区间修改问题,可以用一颗二叉树存储数据的和,从而实现快速修改的操作。
(图片来自网络)
大概原理如上图。通过建立这样一棵树,实现了对于区间部分和的存储。线段树也是解决区间问题的重要手段。
However,本题只要求我们进行单点修改+区间求和,因此我们可以在线段树的基础上更简化一步。
众所周知,和-加数=加数,因此在两个加数与和中,我们只存储其中的两个元素,就可以推出第三个。而在1中的暴力法,我们可以看做存储了两个加数,复杂度表现不佳。线段树则是完全存储了三个数据,在两个数组下标中存储三个数据,显然要进行递归建树等,代码难度较高,且常数较大。我对树状数组的理解是:只存储一个加数以及和,同时通过谨慎地选取,使得每个节点值都是一个特殊区间的和(加数自己的和也是和),再通过巧妙的运算降低复杂度。原理如图。
(图片来自网络)
如图所示:
c1=a1,c2=a1+a2,c3=a3,c4=a1+a2+a3+a4,c5=a5,c6=a5+a6,c7=a7,c8=a1+a2+a3+a4+a5+a6+a7+a8。这样便形成了一个树状数组。
树状数组的原理
观察上面的例子,我们发现:
对于每个树状数组(以下记作数组t)节点i,其值t[i] = a[i - 2k+1] + a[i - 2k+2] + ... + a[i],其中k为i的二进制中从最低位到高位连续零的长度。下面我们简要证明这一点:
通过前面的分析,我们知道,树状数组节点的选取就是将线段树的所有右儿子去掉取得的,如果我们定义一个量指代从叶子节点开始算起向上的树高度,由于每个右儿子对应一个“2”,也就是对应了一个二进制数位,我们不难发现,数字的二进制最低位到最高位零的长度(称为k)正好是这一高度。比如,对于节点6,其k值为1,它的第一个父节点为上一节点的右儿子,再向上一层则是左儿子。这样,树状数组的各个值我们就构造出来了。
(不难发现指我只能找规律发现,并不会用数学方法证明)
那么,树状数组如何实现区间求和呢?
首先思考从节点1到节点n的求和。我们从n开始入手,Sn=tn+(a1+...+a[n-2^k])。然后循环往复,令n=n-2^k,一直相加直到n<=0,就得到了所有节点的和。
现在的关键问题是,2^k我们仍然不方便求解啊,用循环的方法的话,最终的复杂度并不能得到保证。
而这,也是树状数组,或者说二进制数的神奇之一。
记lowbit(k)=2^k,则有lowbit(k)=i&(-i)。(i指的是元素下标,由上述定义,我们知道每个i都对应了一个k)下面给出简要证明:
我们设i这个数字的二进制表示中,r1,r2,.....rn数位的值为“1”,其余数位为“0”(定义最后一位为第0位),则有:i=2^r1+....+2^rn。
由上述定义可知,k是i的二进制表示中从最低位到最高位连续0的高度,因此有k=rn。
而由于负数是由补码表示的,对于(-i),我们知道,由于前面有rn位为0,取反后是1,再+1后让rn个1进位,又第rn+1位是1,取反后是0,进位后得到1,因此得到了第rn+1位是1,后面rn位都是0的数字,也就是说后rn+1位数字与原来数字按位与后的结果都为1,而对于大于rn+1数位的数,由于下面没有进位,因此取反后再按位与的结果都为0。因此最终结果就是在第rn+1位得到一个0。那自然有2^k=i(&-i)成立。
正所谓:“文章本天成,妙手偶得之”。我们不得不为二进制数的精妙而折服。
树状数组的建立,修改与查询。
此处只介绍本题用到的单点修改与区间查询。
树状数组的单点修改
建立树状数组同时也是修改树状数组的一部分。建立树状数组就是把树状数组各个值从0修改到需要值,因此,我们只需要了解如何进行树状数组的单点修改。
void add(int x,int k){for(;x<=n;x+=x&-x){t[x]+=k;}}
从前面的原理分析我们知道,修改一个节点的值,需要对其涉及到的所有值都进行修改。刚才我们在求解t[i]的表达式时,对于每个节点,减去其对应的lowbit得到它需要的作用域(也就是i-2^k+1到i),那么一直递归下去(令i=i-2^k),就可以推到节点1,那么反过来看,我们只要把当前节点一直循环加上lowbit,就可以反推出所有带有这个节点值的节点。
树状数组的区间查询
int sum(int x){int sum=0;for(;x;x-=x&-x)sum+=t[x];return sum;}
我们前面提到了求解1-n区间的和。容易得出,从m到n的求和是Sn-Sm。
彩蛋
某著名oier锐评13行代码:
容我再去优化
我用13行摆烂了你的oj4的更多相关文章
- 无情摆烂我竟是cv怪物第四周周末总结
无情摆烂我竟是cv怪物第四周周末总结 函数重要参数补充 1.*args 星号代表接收未被位置形参接收的额外的位置实参,无论有多少位置实参*args都可以将它全部接受 def func(*args): ...
- 我用了13行代碼開發出来的PHP框架
我只用13行代碼開發的PHP框架,如果您對框架不理解,不知道框架究竟幫您做了什麽事,可以下載此框架看一下, 另外如果您想開發自己的框架也可以由這個框架的思路進行擴展. 源碼下載地址:http://do ...
- 使用13行Python代码实现四则运算计算器函数
原创的刷新行数记录的代码!!! 支持带小括号,支持多个连续+-号,如-7.9/(-1.2-++--99.3/-4.44)*---(2998.654+-+-+-(+1.3-7.654/(-1.36-99 ...
- C# 13行代码带你模拟登录QQ空间
最近想做一个QQ空间点赞的小工具,于是晚上下班回来就开始分析PC版的QQ空间,打开Chrome,切换到Network,然后输入账号密码,然后点击登录... 然后,我曹....一堆请求就开始了....搞 ...
- 13行代碼開發出来的PHP框架[转]
<?PHP /** PHP極簡框架 交流: QQ群: 223494678 http://7di.net 用法 http://URL http://URL/hello http://URL/sev ...
- 13行代码实现:Python实时视频采集(附源码)
一.前言 本文是<人脸识别完整项目实战>系列博文第3部分:程序设计篇(Python版),第1节<Python实时视频采集程序设计>,本章内容系统介绍:基于Python+open ...
- CodeForces 摆烂寄录
按订正顺序排序 现在是乱排的了 完整代码占版面 所以只放 AC 记录链接 Good Bye 2021: 2022 is NEAR 这场打得真拉/tuu A. 简单签到 开场就读错题,浪费 5min / ...
- 摆烂期的Android学习笔记一
Android大致分为四层架构1.Linux内核层:提供各种硬件驱动,如显示驱动,音频驱动,相机驱 动,蓝牙驱动.... 2.系统运行库层:通过C/c++库为android地图提供支持 3.应用框架层 ...
- ASP.NET Core 中文文档 第三章 原理(13)管理应用程序状态
原文:Managing Application State 作者:Steve Smith 翻译:姚阿勇(Dr.Yao) 校对:高嵩 在 ASP.NET Core 中,有多种途径可以对应用程序的状态进行 ...
随机推荐
- catkin编译系统
引言 最近项目中遇到一个需求:将 C++ 程序 (不是 ROS node,只是普通的 C++ 程序)中的变量发布到 ROS topic 上,以便 ROS 中的其他 node 进行后续处理. 原 C++ ...
- 前端规范(ES6BEMOOCSSSMACSS)
前端规范 在实际开发中,由于团队成员编码习惯不一,技术层次不同,开发前定制并遵循一种代码规范能提高代码质量,增加开发效率. Javascript Javascript规范直接参考airbnb: ES6 ...
- 使用Bootstrap typeahead插件实现搜索框自动补全的配置参数。
示例代码: <input type="text" id="addr"/> <input type="text" hidde ...
- ES6-11学习笔记--Symbol
Symbol:一种新的原始数据类型 声明方式: let s1 = Symbol() let s2 = Symbol() console.log(s1); // Symbol() console.l ...
- prometheus之查询语言
PromQL(Prometheus Query Language)是 Prometheus 自己开发的表达式语言,语言表现力很丰富,内置函数也很多.使用它可以对时序数据进行筛选和聚合. 一.PromQ ...
- java中Array(数组)的用法
8.Array(数组) 数组是作为对象来实现的.(really occupy the memopry,真实的占用内存 ) An array is a data structure that st ...
- 【论文阅读】ICLR 2022: Scene Transformer: A unified architecture for predicting future trajectories of multiple agents
ICLR 2022: Scene Transformer: A unified architecture for predicting future trajectories of multiple ...
- Mybatis映射文件动态SQL语句-02
foreach UserMapper.xml <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYP ...
- linux磁盘之lsblk命令
lsblk命令可以显示很多跟磁盘相关分区.所属关系以及lvm的重要信息,所以这个命令最好掌握.lsblk命令默认情况下将以树状列出所有块设备,包括查看磁盘挂载信息.lsblk命令包含在util-lin ...
- springboot打包时候忽略编译测试类
方法1.可以在依赖中加入插件 <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId ...