莫队学习总结

小Z的袜子 (HYSBZ-2038)

作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿。终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L?尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬。 你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子。当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己选择。

思路:模板题

某个区间\([l,r]\)的答案:
\[
{\sum_{i为出现的颜色} sum[i]\cdot (sum[i]-1)/2}\over {C_{r-l+1}^2}
\]

  1. 对询问排序

    1. \([l,r]\),以\(l\)所在块的编号为第一关键字,r为第二关键字从小到大排序。
  2. 暴力维护答案的分子部分即可
  3. 可以发现答案分子分母同时将2约掉,分子展开后变成\(sum[i]\cdot sum[i] - sum[i]\)可以发现对于所有的 i ,\(sum[i]\)的和将变成\(r-l+1\),所以我们只需要维护所有\(sum[i]\)的平方和即可
#include <iostream>
#include <algorithm>
#include <math.h>
#include <cstdio>
using namespace std;
const int N = 50010;
typedef long long ll;
int a[N],be[N],n,m;
ll res = 0,sum[N];
struct node{
    int l,r,id;
    ll A,B;
}q[N];
//如果在同一块,则按照右端点排序,否则按照左端点
bool cmp1(node a,node b){
    return be[a.l] == be[b.l] ? a.r < b.r : a.l < b.l;
}
bool cmp(node a,node b){
    return a.id < b.id;
}
ll gcd(ll a,ll b){return b == 0 ? a : gcd(b,a%b);}
ll S(ll x){return x * x;}
//先减去上一次的影响,修改后再重新加新的影响
void move(int pos,int add){res -= S(sum[a[pos]]);sum[a[pos]] += add;res += S(sum[a[pos]]);}
int main(){
    scanf("%d%d",&n,&m);
    int base = sqrt(n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);q[i].id = i;
        be[i] = (i-1) / base + 1;//be[i]即i在第几块
    }
    for(int i=1;i<=m;i++)scanf("%d%d",&q[i].l,&q[i].r);
    sort(q+1,q+1+m,cmp1);
    int l = 1,r = 0;
    res = 0;//res为当点询问区间内出现的所有颜色的个数平方和
    for(int i=1;i<=m;i++){
        //暴力调整区间,维护res
        while(l < q[i].l)move(l++,-1);
        while(l > q[i].l)move(--l,1);
        while(r < q[i].r)move(++r,1);
        while(r > q[i].r)move(r--,-1);
        if(l == r){
            q[i].A = 0;q[i].B = 1;continue;
        }
        q[i].A  = res - (r - l + 1);//计算答案分子部分
        q[i].B = 1ll * (r - l + 1) * (r - l);//分母部分
        ll g = gcd(q[i].A,q[i].B);//约分
        q[i].A /= g;
        q[i].B /= g;
    }
    sort(q+1,q+1+m,cmp);
    for(int i=1;i<=m;i++){
        printf("%lld/%lld\n",q[i].A,q[i].B);
    }
    return 0;
}

普通莫队优化

可以发现当第一块内的询问处理完之后,r的位置应该特别靠后,但是当移动到下一个块之后,r可能会往前移动很多,比如如下询问

//第一个块
1 50
2 100
//第二个块
12 13
14 100

在完成[2,100]的询问后,r从100-> 13 然后又从13 -> 100。这样显然不如100->100, 100 -> 13。

如何优化?

相邻两块之间r的排序规则相反即可

即奇数块按照升序,偶数快按照降序

Result Memory Time
Accepted 3456 kb 1840 ms
Accepted 3456 kb 1392 ms

下面的是优化过的。

bool cmp1(node a,node b){
    return be[a.l] == be[b.l] ?
        (be[a.l]&1 ? a.r < b.r : a.r > b.r)
        : a.l < b.l;
}

带修改的莫队

考虑普通莫队加入修改修做,如果修改操作可以\(O(1)\)的应用以及撤销(同时也要维护当前区间的答案),那么可以在\(O(n^{5\over 3})\)的复杂度内求出所有询问的答案。

实现: 离线后排序,顺序遍历询问,先将时间转移到当前询问的时间,然后再像普通莫队一样转移区间。

排序方法: 设定块的长度为\(S_1,S_2\),按照(\(\lfloor{l\over S_1}\rfloor \lfloor{r\over S_2}\rfloor,t\))的三元组小到大排序,其中 \(t\) 表示这个询问的时刻之前经历过了几次修改操作

复杂度分析:考虑询问序列中的每个小块,小块内每个询问的一二关键字相同。在这个小块内,时间 \(t\) 最多变化 \(m\) ,对于每个询问,\(l,r\) 最多变化 \(S_1,S_2\), 一共右\(n^2\over {S_1,S_2}\) 个这样的块,相邻块之间转移复杂度是\(O(n)\), 总复杂度就是

\(O(mS_1+mS_2+{n^2m\over S_1S_2}+{n^3\over S_1S_2})\)

当\(n,m\)同阶时,取\(S_1 = S_2 = n^{2\over 3}\) 时可达到最优复杂度\(O(n^{5\over 3})\)

int l = 0, r = 0, t = 0, nowAns = 0;

inline void move(int pos, int sign) {
    // update nowAns
}

inline void moveTime(int t, int sign) {
    // apply or revoke modification
    // update nowAns
}

void solve() {
    BLOCK_SIZE = int(ceil(pow(n, 2.0 / 3)));
    sort(querys, querys + m);
    for (int i = 0; i < q1; ++i) {
        const query q = querys[i];
        while (t < q.t) moveTime(t++, 1);
        while (t > q.t) moveTime(--t, -1);
        while (l < q.l) move(l++, -1);
        while (l > q.l) move(--l, 1);
        while (r < q.r) move(r++, 1);
        while (r > q.r) move(--r, -1);
        ans[q.id] = nowAns;
    }
}

原文地址:https://www.cnblogs.com/1625--H/p/11329586.html

时间: 2024-08-30 18:04:07

莫队学习总结的相关文章

莫队学习总结 &amp;&amp; P1903 [国家集训队]数颜色 / 维护队列

关于时间复杂度 对于多维莫队的复杂度差不多为\(O(n^{\frac{2k-1}{k}})\) 摘自zhihu大佬 奇偶分类优化 return a.l == b.l ? (a.l & 1) ? a.r<b.r: a.r>b.r : a.l < b.l; 这样能快是因为指针移到右边后不用再跳回左边,而跳回左边后处理下一个块又要跳回右边,这样能减少一半操作,理论上能快一倍 思路 好久的题目了,之前其实只会点不修改莫队 对带修改的不大会,也就做过这一个题目 今天重新做了一边,算是有了新

[国家集训队][bzoj2120] 数颜色 [带修改莫队]

题面: 传送门 思路: 这道题和SDOI2009的HH的项链很像,只是多了一个修改 模板套上去呀 莫队学习请戳这里:莫队 Code: 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorithm> 5 #include<cmath> 6 using namespace std; 7 inline int read(){ 8 int re=0,fla

F. Machine Learning 带修端点莫队

F. Machine Learning time limit per test 4 seconds memory limit per test 512 megabytes input standard input output standard output You come home and fell some unpleasant smell. Where is it coming from? You are given an array a. You have to answer the

【bzoj2038-小z的袜子】莫队算法

莫队例题. 莫队学习:https://www.cnblogs.com/Paul-Guderian/p/6933799.html 本题 分子是sigma(c(sum[a[i]],2)),分母是sigma(l-r+1,2); 维护分子和即可. 莫队适用范围:离线,区间,区间转移到下一格O(1). 1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #i

BZOJ 2038: [2009国家集训队]小Z的袜子(hose)【莫队算法裸题&amp;&amp;学习笔记】

2038: [2009国家集训队]小Z的袜子(hose) Time Limit: 20 Sec  Memory Limit: 259 MBSubmit: 9894  Solved: 4561[Submit][Status][Discuss] Description 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两

BZOJ - 3757 树上莫队解决离线路径问题 &amp; 学习心得

题意:给你一棵树,求u,v最短路径的XXX(本题是统计权值种类) 今天课上摸鱼学了一种有意思的处理路径方式(其实是链式块状树翻车了看别的),据说实际运行跑的比XX记者还快 大概就是像序列莫队那样 首先是对暴力查询的优化 第一关键字是块(树上分块),第二关键字是dfs序,这样保证了离线操作的下界最优 其次是转移的优化 我把大佬的话再转述一遍: 设\(S(u,v)\):\(u-v\)最短路径所覆盖的点集 \(S(u,v)=S(root,u)⊕S(root,v)⊕lca(u,v)\) 记\(T(u,v

[知识学习] 莫队

迟到的莫队知识总结 最近学习了一种说优雅也挺优雅,说暴力也挺暴力的算法:莫队算法 普通莫队 例题 给出一个长度为\(n\)的数列,\(a_1,a_2,...,a_n\),有\(q\)个询问,每个询问给出数对\((i,j)\),需要你给出\(a_i,a_{i+1},...,a_j\)这一段中有多少不同的数字 当然你也可以做HH的项链,题意是一样的,只是这个题目卡了莫队算法 首先考虑一下暴力 对于每个询问,直接从\(l\)到\(r\)扫一遍,统计 复杂度\(O(nq)\) 显然不行(废话) 考虑优化

[bzoj2038]莫队算法学习

解题关键:莫队最重要的是区间之间以$O(1)$的复杂度进行转化,由于电脑原因,后续补上公式推导. 1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cstdlib> 5 #include<iostream> 6 #include<cmath> 7 using namespace std; 8 typedef long long ll; 9 l

莫队算法学习笔记【BZOJ2038:小Z的袜子】【SPOJ3267:D-query】

很久以前傻乎乎地看来源奇怪的资料的时候被各种曼哈顿弄晕了. 然后现在学会的是分块方法.另新创一个分块方法. 让我们考虑这样一个区间询问问题…… 它有如下的性质: 0,n个数,Q个询问. 1,它没有修改操作,这意味着我们可以按我们喜欢的次序跟询问玩耍.实际上后面会讲到我们完全可以按任意次序玩耍. 2,如果我们知道区间询问 [L , R] 对应的值,我们可以轻易求出 [L±1 , R] 和 [L , R±1] 的值. (其实如果限制增加,比如只能求 [L+1 , R] 和 [L , R-1] 的值,