莫队阶段小结

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

莫队

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

大体就是这个样子

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

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

时间复杂度为\(O(m\sqrt{n})\)

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

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\sqrt{n}logn\)

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

#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;   
}