骗分大法之-----分块||迷之线段树例题a

什么是分块呢?

就是一种可以帮你骗到不少分的神奇的算法。

分块的写法有几种,我所知道的有①预处理②不预处理

不预处理的代码我看得一脸懵逼

所以我在这里就谈一下预处理的版本www

首先看一道题:

给定一个包含n个数的序列,初值全为0,现对这个序列有两种操作:
操作1:把 给定 第k1 个数改为k2;
操作2:查询 从第k1个数到第k2个数得最大值。(k1<=k2<=n)

所有的数都 <=100000

好的,如果我们搞遍历肯定超时到爆炸。

那么就要用到分块大法了

把这n个数分成若干块,然后每个块计算出最值,并存入数组。

这样在查询的时候只需要遍历所有块的个数,效率++

下面引用梓轩学姐的神奇讲解:

想象一下你现在是一个项目的主管,你要高效地管理所有的员工,那么一个最容易想到的方法就是将他们分组,然后每个组定一个组长。

现在你要询问某些人的最大值,那么如果一个组的所有员工都在询问的范围内,你只需要询问这个组的组长一次就可以知道这个组最厉害的员工是谁而不是一个一个询问,而如果有些组只有其中几个人被询问,那么你还要一个一个地问这几个人。

现在我们把这种思想搬到序列上,很容易就能想到将相邻的元素分到同一个组,也就是我们所说的分块。那么对于询问某个区间,你只需要将这个区间分成若干个完整的块并且记录每个块的最大值,以及两头的若干单独的元素。举个栗子:你有20个元素,你将它们每四个元素分一块,也就是分成了[1,4][5,8][9,12][13,16][17,20]五个块,此时当你询问[6,18]这个区间时只需要询问第6、7、8、17、18个数和第三第四个块的最大值即可,这样我们就免去了一个一个访问中间的元素。

至于修改操作则比较简单,当修改一个元素时,如果它的值增大了,那么我们判断它是否比之前的最大值还大就好,而当被修改的元素是块内的最大值而且它的数值还减小了则比较麻烦,我们只能通过再次遍历一遍整个块来确定最大值。

此时我们来考虑要怎么分块才能更高效地完成工作,如果一个块元素太多那么你要一个一个访问的元素可能很多(比如一个很大的块只有一个元素没被询问到那你得访问遍这整个块除了它以外的元素),如果一个块元素太少那么你可能会要访问好多组。

我们假定S为块的大小,即相邻的每S个元素分为一块,那么我们最多分n/S+1块,假设块的个数是C(很显然它和n/S几乎等价),再来看我们的询问操作是如何进行的:访问所有被询问区间整个包含的块,以及两端的两个不被整个包含的块(可能没有)的被询问的若干元素。前半部分我们会访问O(C)个块,而后半部分我们会访问O(S)个元素。接着还要看修改操作:最坏的情况下我们需要访问整个块,也就是O(S)。

由于O(a+b)=O(max(a,b)),而C是随S递减的,当C=S时才能得到最好的复杂度,也即是S=n^0.5次方。那么一次操作是O(n^0.5)的代价,整个算法的复杂度也就是O(m*n^0.5),由于m和n是同阶的,所以说O(n^1.5)(读作欧恩根号恩)也可以。我们可以看到,只有根号才能让块的元素个数和块的个数达到了均衡,这也就是为什么分块的标志是根号的元素。

回顾整个过程其实非常简单:将序列分块,然后将一个区间询问分解为若干个块和若干个单独元素即可。

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

然后,是例题的代码↓:

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstdio>
 4 #include<cmath>
 5 #include<cstring>
 6 using namespace std;
 7 const int maxx=1000100,inf=1000000000;
 8 int n=0,s=0,c=0,k=0,q=0,w=0;
 9 int a[maxx],f[maxx];
10 int mx[500],zuo[500],you[500];
11 void chu()
12 {
13     s=(int)sqrt((double)n);
14     for(int i=1;i<n;i+=s)
15     {
16         f[i]=++c;
17         for(int j=i+1;j<=i+s-1;j++)
18         {
19             f[j]=f[i];
20         }
21         zuo[c]=i;
22         you[c]=i+s-1;
23     }
24     if(s>0)
25     if(n%s>0)
26     {
27         zuo[c+1]=you[c]+1;
28         you[++c]=n;
29         for(int i=zuo[c];i<=you[c];i++) f[i]=c;
30     }
31
32 }
33 void gai(int x,int v)
34 {
35     int y=f[x];
36         a[x]=v;
37         int temp=-1*inf;
38         for(int i=zuo[y];i<=you[y];i++)
39             if(a[i]>temp)
40                 temp=a[i];
41
42                 mx[y]=temp;
43 }
44 int zui(int l,int r)
45 {
46     int x=f[l],y=f[r],ans=-inf;
47     if(x==y)
48     {
49         for(int i=l;i<=r;i++)
50             if(a[i]>ans)
51                 ans=a[i];
52             return ans;
53     }
54         for(int i=l;i<=you[x];i++)
55             if(a[i]>ans)
56                 ans=a[i];
57             for(int i=x+1;i<y;i++)
58                 if(mx[i]>ans)
59                     ans=mx[i];
60                 for(int i=zuo[y];i<=r;i++)
61                     if(a[i]>ans)
62                         ans=a[i];
63     return ans;
64 }
65 int main()
66 {
67     memset(a,0,sizeof(a));
68     memset(f,0,sizeof(f));
69     memset(mx,0,sizeof(mx));
70     cin>>n;
71     chu();
72     for(int i=1;i<=n;i++)
73     {
74         cin>>k>>q>>w;
75         if(k==1) gai(q,w);
76         if(k==2) cout<<zui(q,w)<<endl;
77     }
78     return 0;
79 }
时间: 2024-10-06 00:16:57

骗分大法之-----分块||迷之线段树例题a的相关文章

ACM-ICPC 2018 沈阳赛区网络预赛 J. Ka Chang(树上分块+dfs序+线段树)

题意 链接:https://nanti.jisuanke.com/t/A1998 给出一个有根树(根是1),有n个结点.初始的时候每个结点的值都是0.下面有q个操作,操作有两种,操作1.将深度为L(根节点深度为0)的点的值全部增加X.操作2.查询以x为根的子树的结点值得和.其中N,Q<=1e5. 思路 因为这题是对某一深度的所有点加x,所以不是树链剖分. 我们可以先预处理一下dfs序,顺带把d[u]:u的深度.dd[x]:深度为x的点集求出来. 考虑分块,对某一深度分两种情况:1.这一深度的点的

HDU - 4366 Successor DFS序 + 分块暴力 or 线段树维护

给定一颗树,每个节点都有忠诚和能力两个参数,随意指定一个节点,要求在它的子树中找一个节点代替它,这个节点要满足能力值大于它,而且是忠诚度最高的那个. 首先,dfs一下,处理出L[i], R[i]表示dfs序,则R[i] - L[i] + 1 就是当前i这个节点拥有的子孙个数. 对于一颗树,dfs的时候,访问节点有先后顺序,那么可以用一个struct node List[maxn];表示这课树中访问的先后顺序. 例如这颗树,我假设是先访问0 --> 3 --> 2 ---> 4 ---&g

线段树例题及做题误区

学会了一系列的线段树之后发现 除了扫描线还不是很熟之外一些操作基本上是得心应手了. 但是仍是很菜,在此再次深有感悟 以后做题再看题解 直接剁手 我就不信不看题解自己的思路出现错误 每次都当我 有了正确的思路之时 却被一些 很迷的思路 误导去看题解,看完题解之后才恍然大悟 .这点需要注意!!!我想我都窥出正解了为什么不能再多想想呢? 真的是超级没有成就感 感觉是非常难受的 好题被自己一时看了题解毁了这是我作为一个正在学习的人所极不想看见的. . 这道题还不错 对线段树是一个考察 如果能仔细思考的话

可持续化线段树(例题Sign on Fence[Codeforces 484E])

刚刚学习的想记录一下: 第一次接触可持续化线段树,很懵... 题目: 题目描述 izon the Champion has recently finished painting his wood fence. The fence consists of a sequence of n panels of 1 meter width and of arbitrary height. The i-th panel's height is hi meters. The adjacent planks

【对询问分块】CODEVS1080 线段树练习

#include<cstdio> #include<cmath> using namespace std; #define N 100001 int sum[N],a[N],n,m,last,op[N],Xs[N],Ys[N]; int main() { scanf("%d",&n); for(int i=1;i<=n;++i) scanf("%d",&a[i]); scanf("%d",&m

清华集训 2014--奇数国(线段树&amp;欧拉函数&amp;乘法逆元&amp;状态压缩)

昨天想了一晚...早上AC了... 这么长时间没打线段树,这回居然一次过了... 感觉数论方面应该已经没有太大问题了... 之后要开始搞动态规划之类的东西了... 题意 在一片美丽的大陆上有100000个国家,记为1到100000.这里经济发达,有数不尽的账房,并且每个国家有一个银行.某大公司的领袖在这100000个银行开户时都存了3大洋,他惜财如命,因此会不时地派小弟GFS清点一些银行的存款或者让GFS改变某个银行的存款.该村子在财产上的求和运算等同于我们的乘法运算,也就是说领袖开户时的存款总

[算法]线段树

拖了好久才写的线段树...... 大概听说它可能实在n年前,在我还是一个孩子的时候[/微笑] 恩大概我觉得有一丢丢丢像分块 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 一 概述 线段树,类似区间树,它在各个节点保存一条线段(数组中的一段子数组)

P2023 [AHOI2009] 维护序列(线段树水题)

题目描述 老师交给小可可一个维护数列的任务,现在小可可希望你来帮他完成. 有长为N的数列,不妨设为a1,a2,…,aN .有如下三种操作形式: (1)把数列中的一段数全部乘一个值; (2)把数列中的一段数全部加一个值; (3)询问数列中的一段数的和,由于答案可能很大,你只需输出这个数模P的值. 输入输出格式 输入格式: 第一行两个整数N和P(1≤P≤1000000000).第二行含有N个非负整数,从左到右依次为a1,a2,…,aN, (0≤ai≤1000000000,1≤i≤N).第三行有一个整

51Nod 算法马拉松15 记一次悲壮而又开心的骗分比赛

OwO 故事的起源大概是zcg前天发现51Nod晚上有场马拉松,然后他就很开心的过去打了 神奇的故事就开始了: 晚上的时候我当时貌似正在写线段树?然后看见zcg一脸激动告诉我第一题有九个点直接输出B就可以A.. 然后之后zcg以奇怪的二分方式发现了如何A掉第一题的第十个点(我记得貌似是什么第5000个数等于511? OwO 就这样没有任何思考含量全凭骗分黑科技过掉了第一题 OwO 然后zcg打开了第二题,发现第二题样例有点问题,然后就发了个帖子,直接去看第三题了 我去瞅了一眼,发现这不是gcd