今天中午,老师带领我们改变了学习策略。要先学习知识点,看题解,学习做题技巧和方法,然后再自己做题,这时候,就算做不出题来,也不能看那题解,同学之间互相商量,找出程序中的不足并解决它。
我先要制定自己的学习计划,我打算看第一个人的博客的时候,要慢慢看,把所有关于这个知识点的方法都掌握牢固,再接着看下一个,这样再看下一个人的博客时,很多相同类型的题,可能看一眼就知道怎么回事了,只需看一下需要注意的地方就可以了,把题浏览一遍估计这道题就算是看完了。
我今天把昨天看的基础知识点又回看了一遍,一是因为感觉掌握的不牢固,二是要看博客了,要把所以得基础知识牢记在心才可以。下面就是我今天的主要收获了,也是我记得笔记,在这里存起来,下次忘记的时候方便查阅。
一。---树状数组的基本函数。
1。lowbit()------算出a&(-a)
2。add()-------更新操作,当有某个值添加或者删除(增加或减少)的时候用。
3。sum()--------求和,sum(i)表示a[1]到a[i]的和。也可以求a[i]的值。
二。----基础操作----区间更新,单点求和。
当[a,b]区间加1时。
可以 add(a,1);从这之后的sum()都会加1。
add(b+1,-1); 从这之后的sum()都会减1。也就是从这之后原来的加一被抵消了。
这样原来的求和数组已经不是原来的意义了,这已经不是为了求和了。
这时候在区间[a,b]内求一个值sum(i)会比原先加1.
三。----基础操作------单点更新,区间区和。
当需要a[i]增加d时,
可以 add(a[i],d);
这样 当 x > i 时,sum(x)求和都增大了d。
求和时,求区间[ j , k ] 的和,sum(k)- sum(j-1);
四----离散化
离散化一般都要定义结构体,结构体里一般包含两个数,一个数是输入的原值 v ,一个数是记录输入的先后次序的 id。
1。在输入数据时,要便输入边对结构体的 id 进行赋值,从1到n。(输入的先后顺序)
2。根据结构体中数据的大小进行排序。(排序)
3。新建立一个数组b,结构体的 id 作为数组下表,然后,根据排序的先后顺序,给数组b赋从1到n的值代表他们原来的大小关系。(大小的代换)
离散化完毕,现在这些不集中的数据,已经变得集中了。在处理数据大,数据量不大的数据时可以采用离散化。
五-----顺序对 (在b[ i ]前面,比b[ i ]小的数的数量)
当离散化完毕后会得到一个数组 b[ i ] , b[ i ] 代表原来值的大小, i 代表输入的先后顺序。(没有离散也是一样的)
求顺序对时,
先求加和 ans+=sum(b[ i ]);
后更新 add(b[ i ] ,1);
sum(b[ i ]) 是比b[ i ]先进入数组 且 比 b[ i ]要小的数,也就是以b[ i ]结尾的顺序对的数量。
ans 就是全部的顺序对的数量。
六-------逆序对(在b[ i ]前面,比b[ i ]大的数的数量)
当离散化完毕后会得到一个数组 b[ i ] , b[ i ] 代表原来值的大小, i 代表输入的先后顺序。(没有离散也是一样的)
先更新 add(b[ i ] ,1);
后求加和 ans+=n- sum(b[ i ]);
n是此时进入数组的数的数量。
sum(b[ i ]) 是比b[ i ]先进入数组 且 比 b[ i ]要小的数。
两者相减就是比b[ i ]先进入数组 且 比 b[ i ]要大的数。(就是以b[ i ]结尾的逆序对的数量)
ans 就是全部的逆序对的数量。
当看完了这个我曾经想过,到底有没有三对上升,或者四对,甚至n对那。果然,那是有的。
七----三对顺序或者逆序。
当一个数存入数组用1来表示,没有存入用0来表示,本身这个树状数组就不是求和了。
因为树状数组的sum1(k)操作,取出的数字,表示比 k 先进入数组的,比k 小的数的个数,也就是输入时,排在k前面比 k 小的数的个数。这是,按照输入顺序放入树状数组的情况。
当倒着把数据放入树状数组时,sum2( k ),取出的数字,就是排在 k 后面的,比k 小的数字的个数。
那么当正着输入时 max1=k - sum1(k)
倒着输入时 max2= n - sum2(k)
此时,max1就是排在 k 前面比k大的数的个数。
max2就是排在 k 后面比 k 大的数的个数。(这个原因在六,逆序对中有)
最后的总数就是 sum1(k)*max2+sum2(k)*max1;
八-----连续上升或下降的n元子序列的求法。(这里说四元上升子序列)
可能用到的知识点 1.离散化(如果数据过大)2 dp(当n>3)时,比如现在是四。3 树状数组 (n个) 4 高精度(如果64位都不够用的话)
输入数据为a[ i ]。
下面的 x y都是a [ i ] 。。。 sum()不是树状数组的函数了,是对里面的所有符合条件的数组求和。
dp过程
dp[ x ][ 2 ] = sum(dp[ y ][ 1 ]);
dp[ x ][ 3 ] = sum(dp[ y ][ 2 ]);
dp[ x ][ 4 ] = sum(dp[ y ][ 3 ]);
其中dp[ x ][ 2 ] 表示的意思是,x为子序列最后一个数字时的二元上升子序列的个数。
因为所有的3元都是来自于之前的二元,所以可以用这个来实现。
其中的树状数组有n个,每次输入一个数据,都需要对所有的树状数组进行更新。
九-----求逆序的一个有意思的题型。(这种题需要离散化之后再做)
从1到n有n个数,叫做 a[ i ] ,杂乱的排成一行,可以算出他们的逆序对数的综合 ans ,但是,现在你需要把a[ 1 ]放到a[ n ]后面,然后再算逆序对数,下一步,把现在的a[ 1 ]放到后面,再算一次,知道循环完毕。要求算出最小的逆序对数。
这时,我们先按照算逆序对数的正常思路,算出ans,
如果第一个移动到最后的时a[ 1 ],在除了a[ 1 ]的所有数中,比a[ 1 ]小的数的个数min=a[ 1 ]-1;
比a[ 1 ]大的数的个数max=n-a[ 1 ];
但是,移动的时候,关于a[ 1 ]的逆序对数改变了,当然其他的没变,原来比它小的,变成了顺序,比它大的变成了逆序。
也就是ans+=max-min;这样就求出了在这一次移动中,ans的变化,然后我们只需要让它循环n-1次,就求出了最小的ans(逆序对的个数)。
这种方法的关键就是a[ i ]是由1到n的,就是需要离散化。
今天学习的主要内容就是这些了,我把我记录的书面笔记,比较条理的整理了一遍,复习了一遍,很多记得不是很清楚的地方都变得清楚了,通过今天一天的学习,我的收货很大,希望我能不断汲取知识,不断成长,让我的内力越来越深厚,招式越来越复杂。
祝明天更美好。