我们首先来看一道例题:
Description
有n个数字,给出k,以及m个查询。
每次查询的格式是L,r,求L~r(左右包含)这个区间内数字的出现次数刚好是k的数字种数。
范围:n<=30000,k<=n,m<=30000,1<=L 输入n,k,m,然后n个元素,然后m行查询,对于每一个询问,输出正确的答案。 Example input: Example output: 我们来考虑一下这题的解法。 首先肯定可以万能暴力,每次L~r枚举。时间复杂度O(N*M)。 但是这样的暴力是没有前途的!我们来考虑一下新的暴力: 一开始指针区间0->0,然后对于一个查询,我们将Left指针逐步更新成新的L,Right同理。 比如一开始Left=2,Right=3,而L=1,r=5。 那么我们Left-1,并且把Left位置上的数字出现次数+1. Right+1,把Right位置上的数字出现次数+1,直到Right=5为止。 框架: 然后以上面题目为例;这种方法需要离线处理,我们同理来看一下解法: 这里说明一下,其实remove(Left);Left--;等等可以直接写成remove(Left--)等等,我这么写是为了理解。 分析一下时间复杂度,我们可以从Left和Right的移动量来分析: 每一个新的询问,Left和Right的移动量最大都会是O(N) 所以这样子的方法时间复杂度仍然是O(N*M),而且可能比上面的暴力更慢。 但是莫队算法的核心,就是从这么一个算法转变过来的。 现在来介绍一下莫队算法解决这道题: 对询问进行分块,我们知道m个询问,L和r的范围都在n以内,我们根据L和r的大小来对询问分块。 比如n=9,有以下的询问: 2 3 1 4 4 5 1 6 7 9 8 9 5 8 6 8 对于n=9,我们以根号n为每个块block的大小,这里block=3. 那么我们把1~3分成一组,4~6,7~9. 对于每一个询问(L,r),我们以L的范围来决定这个询问在哪一个块。 然后每一个独自的块内,我们让询问r更小的排在更前面。 那么上面的询问就可以分组成: (2,3)/(1,4)/(1,6)和 (4,5)/(5,8)/(6,8)和 (7,9)/(8,9) 这一步的排序操作,我们可以在排序的时候加入判断条件cmp: 排序之后,我们再来分析一下时间复杂度;接下来我们会看到神奇的事情!! 刚才分析此方法的时候,我们是从L和R的偏移量分析的;我们仍然用这种方法来分析。 考虑一下在同一个块的时候。由于L的范围是确定的,所以每次L的偏移量是O(√N) 但是r的范围没有确定;r的偏移量是O(N)。 那么从一个块到另一个块呢? 明显地,r我们不需要作考虑,仍然是O(N)。 而L明显最多也是2*√N,而且这种情况下,很快就会到下下一块。所以也是O(√N) 由于有√N(根号N)个块,所以r的总偏移量是O(N*√N) 而M个询问,每个询问都可以让L偏移O(√N),所以L的总偏移量O(M*√N) 注意了,时间复杂度分析的时候一定要注意,r的偏移量和询问数目是没有直接关系的。 而L则恰恰相反;L的偏移量我们刚才也说明了,它和块的个数没有直接关系。 所以总的时间复杂度是: O((N+M)*√N) 很神奇地看到了,我们仅仅改变了一下问题求解的次序,就让时间复杂度大幅度下降! 当然在这个说明过程中我们也看到了,事实上,莫队是一个必须离线的算法。 意味着一些题目如果强制在线,那么莫队就无能为力了。 先面贴一个莫队的典型题目 小Z的袜子 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。 你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子。当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己选择。 然而数据中有L=R的情况,请特判这种情况,输出0/1。 输入格式: 输入文件第一行包含两个正整数N和M。N为袜子的数量,M为小Z所提的询问的数量。接下来一行包含N个正整数Ci,其中Ci表示第i只袜子的颜色,相同的颜色用相同的数字表示。再接下来M行,每行两个正整数L,R表示一个询问。 输出格式: 包含M行,对于每个询问在一行中输出分数A/B表示从该询问的区间[L,R]中随机抽出两只袜子颜色相同的概率。若该概率为0则输出0/1,否则输出的A/B必须为最简分数。(详见样例) 输入样例 输出样例 题解 从所有袜子的数量中选取两个所组成的组合数作为分母,再从每种袜子的总数选取两个作为分子 所以分子为cnt[i]*(cnt[i]-1)的和 i为每种袜子 cnt记录的每种袜子的数量 分母为(r-l+1)*(r-l) 5 2 3
1 2 3 2 2
1 2
2 4
1 5
0 //没有
1 //2
0 //没有(2太多了,也不算)
int add(x)//把x位置的数字加入进来
{
cnt[x]++;
if (cnt[x]==k) ans++;
}
int remove(x)//把x位置的数字移出去
{
cnt[x]--;
if (cnt[x]==k-1) ans--;
}
Left=1;
Right=0;
ans=0;
for(int u=1;i<=b;u++)
{
while (Left
bool cmp(node x,node y){
if ((x/block)!=(y/block))
return x.L
题目描述
输入输出格式
6 4
1 2 3 3 3 2
2 6
1 3
3 5
1 6
2/5
0/1
1/1
4/15
说明
30%的数据中 N,M ≤ 5000;
60%的数据中 N,M ≤ 25000;
100%的数据中 N,M ≤ 50000,1 ≤ L < R ≤ N,Ci ≤ N。
#include
#include