【QBXT】学习笔记——Day7分块

早上起来看到rqy老爷橙名了TAT
我还是好蒻啊,根本打不过。
今天开始直接用markdown编辑0.0

================分割线=================

Day7 1.21AM

今天讲分块,应该很爽。
什么是分块?分块是一种改进的暴力暴力暴力!
上课要讲莫队、树分块、和其他根号n相关的思想0.0

分块如何代替数据结构?

分块与线段树:
单点修改、区间查询0.0,分成 n 个块,然后各种暴力。
区间修改、区间查询?记单点值、区间和、区间标记。

块大小如何选择?——使得复杂度尽可能低。
例如序列总长为n,块大小t,查询 O(nlognt) ,修改 O(t)
询问m次,复杂度就是 O(m(nlognt))

BZOJ1012
末尾插入一个数、求区间最大值

BZOJ3224
插入x,删除x、查询x数的排名、查询排名为x的数
求x的前驱后继。
这不是平衡树吗woc,这么熟悉的赶脚。这您也能分块。
如果我们用线段树做,首先当然是离线,离散化,然后权值线段树。
分块和这个东西就是一样的。。。
每个块记一个范围内出现数字个数什么的,一个个枚举就好了。
还要开一个数组记录每个数字在哪个块之类的,实现问题。

BZOJ2002弹飞绵羊
这不是LCT吗,怎么又他喵可以用分块!
分块真是一种优秀的暴力!
考虑暴力时,就是一步一步走,这样可能是 O(n)
那么考虑分块,我们分成 n 块,预处理弹多少次能弹出块,要弹多少次。
这样查询就是 n 的了。
那么怎么修改呢?
如果我们修改了一个块内的某个点,对其他块是没有任何影响的,
所以我们对于这个块再重新处理一遍,复杂度也是 n 的。

块状链表:
块内用数组存储,块与块之间用链表形式相连
若块大小设置为k,这个k的意思是块大小在k左右0.0
块大小超过2k就分裂成两个块,块大小小于k/2时就与相邻块合并
当然这个2这个常数是可以随便调的啊,看怎样跑得快。
本来就是暴力,随便一点就好了。

上课依照理解码的:

struct Lump{
    Lump *pre,*nex;
    int val[500]//块内信息 
    int cc;//块内元素个数 
}a[500],*beg,*end;//块外部链表结构 
struct Lump *u[500];//内存池
int cnt;
u[i]=a+i; 

Lump *now
now=beg;

now=now->nex;
now->val[x];//插入

if(now->cc>2*k){
    Lump *nx=u[cnt];
    --cnt;

    将val从中间分开,一半放在原地,一半放到nex
//合并后大于2k呢?判断了再拆一次,不判断可能会被卡

    nx->nex=now->nxt;now->nex-pre=nx;
    now->nex=nx;nx-pre=now;
}

if(now->cc2){
    Lump *nx=now-nex;

    合并合并

    nx->pre=now->pre;
    now->pre->nex=nx;

    cnt++; 
    u[cnt]=now;
//合并后大于2k呢?判断了再拆一次,不判断可能会被卡
}


int val[500][500];//第一维是那个块,第二维是信息
int pre[500],nex[500];
int beg,end; //明明是一样的我为什么要再写一次

BZOJ3223:维护一个序列:操作为翻转一个区间。
这不是可以线段树做吗233,splay基本操作。
块状链表也可以做哦:
翻转一个区间时,零散部分暴力修改,整块部分在链表上翻转,
然后这些块打上翻转标记233
一些细节就是翻转之前标记有可能要下传。
有一些暴力的翻转方法就不说了(就是重构链表什么的)。

BZOJ3065:一个序列三种操作
插入一个数,修改一个数,查询区间k小值,强制在线。
主席树??替罪羊树??
我们写一个“简单”的分块。
每个块在内部用“权值分块”的方法,记录每个数出现了多少次。
(其实不是分块套分块嘛)
这个分块后的信息要做前缀和,
也就是每一个块实际记录的是这个块和之前的总和。
查询是二分,然后利用维护的信息判断二分方向。
每t个数一个块,块内的分块没s个数一个块。
修改 O(nt) ,查询 O(logn(t+s+ns))
s=n t=nlogn ,平均一次复杂度 O(nlogn)
老师在BZOJ上跑的差不多是最快的,那些替罪羊树套主席树的都弱爆了233

还有dalao们虐老师的思路。
分块后,每个块记录这个块排序后是怎样的。
插入修改暴力。查询时二分一下x。
因为我们在每个块上都是记录了排序结果,这样块上再二分就可以得到结果。
时间复杂度O(能过) //其实是我没听到辣,在看WenDavid的问题TAT

CF354D:
一个n行当金字塔,可以执行两类操作:
1.花费3的费用对一个格子染色。
2.花费m+2的费用对一个大小为t的金子塔染色,必须包含最后一行。
给出m个格子,要求这m个格子必须被染色,求最小花费。
n,m<=105
我们计算一下这个东西,发现染色子金字塔最多染 (m) 层的啊,如果超过这个层数就暴力dp一下。
这个金字塔是可以斜着分层的,大概像这样啦。
1
1 2
1 2 3
1 2 3 4

f[i][j] ,前i层需要染的格子都染了,j表示不在前i层,染的右边的三角形的高度是多少。(因为可能染色要染一个金字塔,就会多染了右边一部分)
考虑怎么转移。
首先可能是直接将当前的这个(i,j)节点染了色
f[i][j]=f[i-1][k] (k<=j)+cost(i,j)为根的金字塔+第i层其它点单独染的费用。
也可能这个多出来的金字塔是在更高的位置染的,那么这个多出来的金字塔的费用显然是在之前就已经算过了,所以f[i][j]=f[i-1][j-1]
最后的答案就是f[n][0]

用前缀最大值转移。
f[5][6] f[4][1]…f[4][6]
f[5][7] f[4][1]…f[4][7],显然只多了一个f[4][7],这样对于第一个转移就不用枚举k了。
那这样实际上我们要先转移第二个方程
方程大概是下面这个样子的。

【QBXT】学习笔记——Day7分块_第1张图片

Day7 1.21PM

BZOJ2038 莫队经典题
如果现在只知道区间 [a,b] 的答案,同时知道区间中每种颜色的袜子有多少。
可以直接计算得到 [a,b+1] 的答案只需要考虑b+1的情况,
如果用线段树维护区间中袜子的数量,可以 O(logn) b+1 的颜色加上
(然而并不用线段树啊,可以直接暴力改…还快呢…
将询问排序使得移动次数较小。
n 分块,左端点所在块为第一关键字,右端点位置为第二关键字,排序。

莫队算法可以理解为这些询问都是二维平面上的点,
进行算法相当于二维平面上进行移动,移动距离就是复杂度。

带修改的莫队:
我们将时间也看作是一个维度,这样询问就相当于是在三维平面上移动。
移动时间,就是做出修改。
那么现在我们排序以左端点所在块为第一关键字,
右端点所在块为第二关键字排序,时间为第三关键字排序。
但是这样子如果我们两次分块都按 n 分块显然是不行的,这样会变成 O(n2)

我们令块大小为 s ,一共有 ns 个块,
考虑询问不跨越块左右端点分别有 ns 块,一共情况有 (ns)2 种,
那么这样的复杂度就是 (ns)2×n+ns
如果询问跨越块,显然比上一个会少一点。
考虑取什么 s 比较好呢?
经过计算, s=n23 会比较好,这样总复杂度就是 O(n53)
n 6e4 的时候复杂度接近 1e8 ,因此题目范围一般在 5e4 左右。

如果袜子那题改成带修改的,我们还可以预处理一下袜子每个时间是什么颜色的
修改也是可以 O(1) 计算出来答案变动的,减去再加上即可。
balabala~~~

BZOJ2121
一个长度为n的序列,m次操作,
询问一个区间里有多少不同的数或修改一个数,修改不超过1000次。
就是带修莫队啊23332333。

BZOJ1086国家(SCOI2005王室联邦)
国家有n个城市,编号1~n,一些城市之间有道路相连,
任意两个不同城市之间仅有一条直接或间接的道路。
每个省要至少有B个城市,至多3B个。
该省的任意一个城市到达省会所经路上的城市都必须属于一个省。
(就是去掉省会它们不连通了)
然而省会可以不属于这个省233,如果不懂就看原题吧。

dfs,当一个点x的若干子树 >=B ,就分成一块,x是省会。
最后剩下不到B个点传上去,用祖先节点划分块。
这样每个块 <=2B ,最后剩下不到B个放到最后一个块里。

树分块:
用上一题的方法给树分块,类似序列分块方法维护信息,解决树上问题
1、除根节点所在块以外,每一块内深度最小的结点的父亲相同。
这个父亲被称之为该块的块顶,其中特别的根节点也是块顶。
2、每一块内非深度最小的结点的父亲一定与其处于同一块中。
3、 B<=<=3B 。B是你定义的一个常数
(B就是决定块大小和块个数的,修改B值会影响算法的最终复杂度)
也就是说,对于块顶不在块内的结点,加上块顶这个块就联通。

BZOJ3757
树上n个点,每个点有颜色。
m次询问,每次询问一条路径上有多少种颜色。
每个询问给出u,v,a和b,其中u,v表示路径,
a,b表示这个询问认为a和b是一种颜色(即只算一次)。

对于a,b的限制我们特判就可以了。
莫队改成树上的,分块方式就是树分块,右端点排序可以按dfs序
然后如果是询问[a,b]变成询问[c,d],
我们就先让dep小的往上走,再一起往上走什么的

接下来是一些关于 n 的题目。

CC COUNTARI(BZOJ3509)
给出一个长度为n的数列A,求有多少个三元组满足 i<j<k 并且 ajai=akaj
n<=1e51<=ai<=3e4

这是一个卷积的形式,可以用FFT。
如果n遍FFT显然会T,所以我们分块,每块t个数
每块只对左右两边做一次FFT,但这样少算了i或k与j在同一块的情况,特判就行了。
取合适的 t=mlogm ,时间复杂度 O(nmlogm)

根据我的分析,这个东西应该长这样:
ans[2i]=i1d=0sum[id]sum[i+d]
然后我就不会了,留个坑吧,到时学了FFT再写。

CC FNCS
有一个长度为n的数组A,元素编号1~n,还有n个函数,函数编号1~b
第i个函数的返回值是A中编号在L[i]和R[i]之间的数的和。
支持两种操作:将A[x]修改为y,求编号在l和r之间的函数的返回值之和。
1<=n,m<=105
分成 n 块,每一块上记录每个点在这一块出现了多少次,以及这个块当中当前f函数的和。
这一部分可以用 nn 的时空复杂度求出来。
修改的时候扫描每一个块,对每个块可以 O(1) 求出这次修改后这个块的和。
回答询问的时候,对于整块中的和,直接读块中的信息,对于剩下的部分暴力即可。

CC GERALD07
怎么又是这题?昨天讲了就不写了(虽然貌似昨天是LCT,但这题我昨晚就是用莫队写的)

还给了两道练习,顺便也在这口胡。
BZOJ4540
给出一个序列,求某段区间的位置不同子串的最小值之和

虽然今天是讲分块,但是看到了Claris的线段树做法…

最大值和最小值的问题是独立且相似的,考虑最大值:
考虑离线,设 ask(i,l,r) 为以1到i为右端点时左端点在区间[l,r]内的区间最大值的和。
从1到n枚举右端点,假设现在是i,那么可以通过单调栈求出最小的j使得 [j,i] a[i] 是最大值。
然后左端点在 [j,i] 区间内的所有区间最大值都应当变为a[i],且所有答案应该加上本身的值。
通过线段树打标记维护,时间复杂度 O(nlogn)

对于线段树上每个节点,维护以下信息:
v : 区间内所有数的和
s : 历史上所有v的和
l : 区间长度
a,b,c,d : 标记,分别表示生效之后
v=av+bl
s=cv+dl+s

然而这题可以用莫队算法来做。转自这里
莫队算法的关键在于如何计算贡献:当我对于区间 [l,r1] 加入 r 时,会产生 (rl+1) 个子串,
也就会产生这么多新的贡献,接下来我们只考虑这些新贡献。

[l,r] 的最小值所在位置为 t ,则容易发现t能产生的贡献为 (tl+1)a[t]

对于 r 能产生的贡献,不妨设 last[r] r 的左边比 r 小的第一个元素的位置
last 数组显然可以用单调栈 O(n) 的求出),则 r 的贡献为 (rlast[r])a[r] ,以此类推,
last[r] 的贡献为 (last[r]last[last[r]])a[last[r]]

那么我们可以根据上式构造出一个类似于前缀和的东西,
suml[r] 表示r一直按上述操作走到0的每个点的贡献之和,
则可以用 suml[r]=suml[last[r]]+(rlast[r])a[r] 进行递推。
这样一来我可以在 O(1) 的时间里面完成对r加入所产生的贡献的影响。

r 删除是等价的,只要变成减去就可以了。至于l的情况,完全类似。

BZOJ4401
给定一颗树,对树进行树分块使得每块点数相同,求方案数

首先,块的大小确定的话,可以发现方案最多只有1种
然后就可以 O(nn) 搞,不过会TLE
接着我们又发现,一个节点可以作一个块的根,当且仅当该节点的size能被块的大小整除
然后就可以 O(nlogn) 搞了

下午快走的时候讲了这个东西:
矩阵也是可以分块的:A*B
考虑矩阵乘法的算法,我们将A矩阵的行分块,那么在B的对应列也要分块。
而可以单独将A矩阵的列分块,也可以单独将B矩阵的行分块。
然而这个东西一般不会有什么用,因为复杂度不会下降啊。。

矩阵乘法的时候有时候可以直接跳过一个循环,比如一个重复利用的乘数是0的时候什么的…

分块对角矩阵的行列式与逆?
不好意思我的线性代数还没有开始看呢。
行列式的话,
二阶行列式就是两个列向量,然后平行四边形的面积。
三界行列式就是三个列向量,然后立体六面体的体积。
矩阵的逆:
A×A(1)=I I 是单位矩阵

线性性:两个n列的行列式的和就是对应列的向量的和构成的行列式。
交错性:如果行列式中有两个向量相同,那么值为0。
规范性:单位矩阵的行列式等于1

上面这些关于数学的东西我并不是很清楚,欢迎指正0.0

D=(1)ka1k1a2k2...ankn

上/下三角矩阵的行列式等于对角线的权值乘积。
然后我们发现高斯消元不改变行列式。
所以我们可以 O(n3) 求行列式的值。

回到分块对角矩阵:
一个矩阵划分,每个矩阵划分后的小矩阵都是长宽相等。
那么这个矩阵的行列式的值,就是这些矩阵的行列式的乘积。

为什么后面要讲线性代数这么可怕的东西

Day7 1.21NIGHT

晚上上了一会课,讲了一页ppt

求相邻两个数的差为1的序列的RMQ(当然可以差为x)
这个分块可以 O(n) 难以置信的发现

L=logn2 ,每一块就是 nL
首先对每一个块,求出每个块的max
然后我们对这些块做RMQ
f[i][j] 表示 ii+2j1 这些块的max
时间复杂度 nL×log(nL) ,我们可以看作 nL×logn ,就是n的
那么零散的部分怎么求呢?
对于每个块,我们都可以表示为+1-1这样子
显然本质不同的块是 2(L1) = n
显然,对于每个块的内部最大值的位置都是固定的。
那么我们只要预处理一下这每一种区间的最大值在哪里就行了。
L2 种区间(头尾的位置),处理最大值的复杂度是 L3
但是其实处理中 [i,j] 可以直接 O(1) 转移到 [i,j+1] 的,这样就是 L2
所以预处理是 n×log2n ,也就可以看作是 O(n)

还有一个这个神奇的算法,不过我看不懂英文也没什么人写。
Method of four Russians (四个俄罗斯人???)

然后又讲了平面最近点对。
给出n个点对,目标令所有点按y排序顺便得出最近点对。
所有点按x排序,然后中间分开
考虑我们左右分开的两边已经有序,如何合并。
令a>=左边所有点对的距离,a>=右边所有点对的距离。
然后我们将中轴线左右两边各划分出一列边长为a的正方形。
显然这些正方形的个数是常数个的(7个以内),正方形内的点数是常数个的。
对于这些点,我们就暴力看一看正方形内所有点的距离。
然后总的复杂度是 O(nlogn)

O(n) 的算法,但可能被卡
记d为当前最近点对距离。
先得出 d=dis(a1,a2) ,然后将平面划分成无数个 d×d 的格子。
每个格子给一个 hash 值。
我们可以判断一下 a3 在不在 a1 a2 相邻的八个格子里,
如果不在就加入 a3 这个点,如果在就计算一下这个nd是不是小于d了。
如果是的话,我们用nd,暴力重构分割这个平面。

对于新加入的第i个点重构的复杂度是 O(i) ,重构的概率是 2i (前i个点里最小值)然后期望均摊是 O(1) 重构。

哈希的正确方法?
一般的hash,正确与否与数据有关,在CF这种地方别人可以看着你的代码卡hash。
我们可以采用这样一种方法,使得不论数据是什么,冲突的概率都是 1p
可能下面是错的
S=ni=0ai×xi ,其中x是字符串的每一位,ai随机给就行了。这就是一个十分优秀的哈希233,证明忘了记。

你可能感兴趣的:(学习知识up,学习笔记)