莫队阶段小结

Posted wyxdrqc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了莫队阶段小结相关的知识,希望对你有一定的参考价值。

莫队阶段小结

首先,为什么要叫小结呢,因为我只学了一点点,后续可能更多

莫队

莫队是一种离线处理区间问题的神器.答题思路就是你将原数列分成\(\sqrtn\)块,将所有查询左端点定位,并按照左端点所在的块进行排序,相同则按照右端点排序

大体就是这个样子

inline bool cmp(Q x,Q y)
    return belong[x.li] == belong[y.li] ? x.ri < y.ri : x.li < y.li;

这样的话我们每个快内都暴力求

时间复杂度为\(O(m\sqrtn)\)

但是还有一种玄学加速方式,十分有用

inline bool cmp(Q x,Q y)
    return belong[x.li] ^ belong[y.li] ? belong[x.li] < belong[y.li] : belong[x.li] & 1 ? x.ri < y.ri : x.ri > y.ri;

会快很多.

之后我们每次维护取件区间,和当前左右端点.每次发现不在当前区域内就暴力跳的同时修改信息

  int nowl = 1,nowr = 0;
    for(int i = 1;i <= m;++i)
        int L = q[i].li,R = q[i].ri;
        while(nowl > L) add(--nowl);
        while(nowr < R) add(++nowr);
        while(nowl < L) del(nowl++);
        while(nowr > R) del(nowr--);    
        //统计答案
    

另外注意应当先拓展后收缩,为了该开始改成负数之类的东西。

接下来放几道例题

luoguP3901 数列找不同

莫队的板子题了,我们维护\(sum_i\)表示\(i\)这个数当前出现了多少次,\(ans\)表示当前出现次数\(>=2\)的数的个数

每次修改时更新\(sum\)\(ans\),之后统计答案即可

#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
using namespace std;
const int N = 1e5 + 3;
int a[N],ans[N];
int sum[N];
int n,m,now = 0,bl;
int belong[N],block;
inline int read()
    int v = 0,c = 1;char ch = getchar();
    while(!isdigit(ch))
        if(ch == '-') c = -1;
        ch = getchar();
    
    while(isdigit(ch))
        v = v * 10 + ch - 48;
        ch = getchar(); 
    
    return v * c;

struct Q
    int li,ri;  
    int id;
q[N];
inline bool cmp(Q x,Q y)
    return belong[x.li] == belong[y.li] ? x.ri < y.ri : x.li < y.li;

inline void add(int x)
    x = a[x];
    ++sum[x];
    if(sum[x] == 2) now++;

inline void del(int x)
    x = a[x];
    --sum[x];
    if(sum[x] == 1) now--;

int main()
    n = read(),m = read();
    for(int i = 1;i <= n;++i) a[i] = read();
    bl = sqrt(n);
    for(int i = 1;i <= n;++i) belong[i] = i / bl;
    for(int i = 1;i <= m;++i) q[i].li = read(),q[i].ri = read(),q[i].id = i;
    sort(q + 1,q + m + 1,cmp);
    int nowl = 1,nowr = 0;
    for(int i = 1;i <= m;++i)
        int L = q[i].li,R = q[i].ri;
        while(nowl < L) del(nowl++);
        while(nowl > L) add(--nowl);
        while(nowr > R) del(nowr--);
        while(nowr < R) add(++nowr);
        ans[q[i].id] = now;
    
    for(int i = 1;i <= m;++i)
        if(ans[i]) printf("No\n");
        else printf("Yes\n");
    
    return 0;   

CF375D Tree and Queries

虽然是树上问题,但是所有的询问都是针对子树,我们就可以针对他的欧拉序进行操作.
但是比较棘手的是所有的\(k_i\)可能不相同,

我们就设\(sum_i\)表示\(>=i\)的数的个数

\(val_i\)表示\(i\)的出现次数

每次修改统计即可

答案就是\(sum[k_i]\)

#include<cstdio>
#include<cctype>
#include<algorithm>
#include<vector>
#include<cstring>
#include<iostream>
#include<cmath>
using namespace std;
const int N = 1e5 + 3;
int n,m;
vector <int> G[N];
int v[N],sum[N],val[N];
int l[N],r[N],c[N];
int belong[N];
int ans[N],tot,cnt,bl;
struct Q
    int li,ri;
    int id;
    int ko; 
q[N];
inline int read()
    int v = 0,c = 1;char ch = getchar();
    while(!isdigit(ch))
        if(ch == '-') c = -1;
        ch = getchar();
    
    while(isdigit(ch))
        v = v * 10 + ch - 48;
        ch = getchar(); 
    
    return v * c;

inline void dfs(int x,int f)
    c[++cnt] = v[x];
    l[x] = cnt;
    for(int i = 0;i < (int)G[x].size();++i)
        int y = G[x][i];
        if(y == f) continue;
        dfs(y,x);   
    
    r[x] = cnt;

inline bool cmp(Q x,Q y)
    return belong[x.li] == belong[y.li] ? x.ri < y.ri : x.li < y.li;    

//sum[i]:>=i的数的种类数
//val[i]:i出现次数 
inline void add(int x)
    x = c[x];
    val[x]++;
    sum[val[x]]++;

inline void del(int x)
    x = c[x];
    sum[val[x]]--;
    val[x]--;

int main()
    n = read(),m = read();
    for(int i = 1;i <= n;++i) v[i] = read();
    for(int i = 1;i < n;++i)
        int x = read(),y = read();
        G[x].push_back(y);G[y].push_back(x);    
    
    dfs(1,0);
//  cout << "GG" << endl; 
//  for(int i = 1;i <= n;++i) printf("%d %d\n",l[i],r[i]);cout << "GG"  << endl;
    bl = sqrt(n);
    for(int i = 1;i <= n;++i) belong[i] = i / bl;
    for(int i = 1;i <= m;++i)
        int x = read();
        q[i].li = l[x];
        q[i].ri = r[x];
        q[i].ko = read();
        q[i].id = i;
    
    sort(q + 1,q + m + 1,cmp);
    int nowl = 1,nowr = 0;
    for(int i = 1;i <= m;++i)
        int L = q[i].li,R = q[i].ri;
        while(nowl > L) add(--nowl);
        while(nowl < L) del(nowl++);
        while(nowr < R) add(++nowr);
        while(nowr > R) del(nowr--);
        ans[q[i].id] = sum[q[i].ko];
    
    for(int i = 1;i <= m;++i) printf("%d\n",ans[i]);
    return 0;   

luoguP4396 [AHOI2013]作业

这道题和上几道不大一样,发现不了区间限制之外,还有值域的限制

我们就用树状数组维护值域

但是这样的时间复杂度为\(n\sqrtnlogn\)

其实跑\(10^5\)是挺虚的,还好题目\(3s\),这道题有\(n\sqrtn\)的做法,先咕一咕

#include<cstdio>
#include<cstring>
#include<cctype>
#include<iostream>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
const int N = 1e5 + 3;
int a[N],ans[N],belong[N];
int ans2[N],ans1[N];
int n,m,bl;
int maxx;
int book[N];
struct Q
    int li,ri;
    int ai,bi;
    int id;
q[N << 1];
struct BIT
    int c[N << 3];
    inline void add(int x,int v)
        for(;x <= maxx;x += x & -x) c[x] += v;    
    
    inline int query(int x)
        int res = 0;
        for(;x;x -= x & -x) res += c[x];
        return res; 
    
;
BIT T1,T2;
inline int read()
    int v = 0,c = 1;char ch = getchar();
    while(!isdigit(ch))
        if(ch == '-') c = -1;
        ch = getchar(); 
    
    while(isdigit(ch))
        v = v * 10 + ch - 48;
        ch = getchar(); 
    
    return v * c;

inline bool cmp(Q x,Q y)
    return belong[x.li] ^ belong[y.li] ? belong[x.li] < belong[y.li] : belong[x.li] & 1 ? x.ri < y.ri : x.ri > y.ri;

//int ans = 0;
inline void add(int x)
    x = a[x];
    if(book[x] == 0) T2.add(x,1);
    book[x]++;
    T1.add(x,1);

inline void del(int x)
    x = a[x];
    if(book[x] == 1) T2.add(x,-1);
    book[x]--;
    T1.add(x,-1);

int main()
    n = read(),m = read();
    bl = sqrt(n);
    for(int i = 1;i <= n;++i) 
        a[i] = read() + 1,belong[i] = i / bl;
        maxx = max(maxx,a[i]);
    
    for(int i = 1;i <= m;++i)
        q[i].li = read(),q[i].ri = read();
        q[i].ai = read() + 1,q[i].bi = read() + 1;  
        if(q[i].ai > q[i].bi) swap(q[i].ai,q[i].bi);
        q[i].id = i;
        maxx = max(q[i].ai,max(maxx,q[i].bi));
    
    sort(q + 1,q + m + 1,cmp);
    int nowl = 1,nowr = 0;
    for(int i = 1;i <= m;++i)
        int L = q[i].li,R = q[i].ri;
        while(nowl > L) add(--nowl);
        while(nowr < R) add(++nowr);
        while(nowl < L) del(nowl++);
        while(nowr > R) del(nowr--);    
        ans1[q[i].id] = T1.query(q[i].bi) - T1.query(q[i].ai - 1);
        ans2[q[i].id] = T2.query(q[i].bi) - T2.query(q[i].ai - 1);
    
    for(int i = 1;i <= m;++i) printf("%d %d\n",ans1[i],ans2[i]); 
    return 0;   

以上是关于莫队阶段小结的主要内容,如果未能解决你的问题,请参考以下文章

莫队算法小结以及模板题

软件工程综合实践阶段小结

学习阶段小结 8.1

阶段小结

Android 手机卫士--阶段小结1

2017国赛小结