莫队算法

http://www.cnblogs.com/hzf-sbit/p/4056874.html

https://www.zhihu.com/question/27316467/answer/36260465

处理一类无修改的离线区间询问问题

复杂度为O(n*sqrt(n)*a),a为单次更新操作的复杂度。

hihocoder1488

 

#include<bits/stdc++.h>
using namespace std;

const int N = 2e4+20;
int a[N];

long long ans[N], now;

struct Q{
    int l, r, p;
}q[N];

int c_num[N], c_time[N];
pair<long long, long long> sum(int x) {
    long long num = 0, time = 0;
    while(x) {
        num += c_num[x];
        time += c_time[x];
        x -= x&-x;
    }
    return {num, time};
}
void add(int x, int d) {
    for(int i = x; i < N; i += i&-i) {
        c_num[i] += d;
        c_time[i] += d > 0? x: -x;
    }
}

void add(int x) {
    x = a[x];
    auto u = sum(x), v = sum(N-1);
    now += (v.first-u.first)*x+u.second+x;
    add(x, 1);
}
void del(int x) {
    x = a[x];
    add(x, -1);
    auto u = sum(x), v = sum(N-1);
    now -= (v.first-u.first)*x+u.second+x;
}

int main() {
    int t; scanf("%d", &t);
    while(t--) {
        memset(c_num, 0, sizeof(c_num));
        memset(c_time, 0, sizeof(c_time));
        now = 0;

        int n, m;
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++)
            scanf("%d", a+i);

        /*莫队算法*/
        int lim = sqrt(n+0.5);
        for(int l, r, i = 1; i <= m; i++) {
            scanf("%d%d", &l, &r);
            q[i] = Q{l, r, i};
        }
        sort(q+1, q+m+1, [&](Q a, Q b){ return a.l/lim == b.l/lim? a.r < b.r : a.l/lim < b.l/lim; });

        for(int i = 1, l = 1, r = 0; i <= m; i++) {
            int L = q[i].l, R = q[i].r;
            while(l < L) del(l++);
            while(l > L) add(--l);
            while(r < R) add(++r);
            while(r > R) del(r--);
            ans[ q[i].p ] = now;
         }
         for(int i = 1; i <= m; i++)
            printf("%lld\n", ans[i]);
    }
    return 0;
}

 

 

 

作者:张瑯小强
链接:https://www.zhihu.com/question/27316467/answer/130423804
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
*********************************
问题:有n个数组成一个序列,有m个形如询问L, R的询问,每次询问需要回答区间内至少出现2次的数有哪些。
*********************************

int len;    // 块长度
struct Query{
    int L, R, ID, block;
    Query(){}  // 构造函数重载
    Query(int l, int r, int ID):L(l), R(r), ID(ID){
        block = l / len;
    }
    bool operator < (const Query rhs) const {
        if(block == rhs.block) return R < rhs.R;  // 不是if(L == rhs.L) return R < rhs.R; return L < rhs.L
        return block < rhs.block;           // 否则这就变回算法一了
    }
}queries[maxm];

map<int, int> buf;

inline void insert(int n){
    if(buf.count(n))
        ++buf[n];
    else
        buf[n] = 1;
}
inline void erase(int n){
    if(--buf[n] == 0) buf.erase(n);
}

int A[maxn];        // 原序列
queue<int> anss[maxm];  // 存储答案

int main(){
    int n, m;
    cin >> n;
    len = (int)sqrt(n);    // 块长度
    for(int i = 1; i <= n; i++){
        cin >> A[i];
    }
    cin >> m;
    for(int i = 1; i <= m; i++){
        int l, r;
        cin >> l >> r;
        queries[i] = Query(l, r, i);
    }
    sort(queries + 1, queries + m + 1);
    int L = 1, R = 1;
    buf[A[1]] = 1;
    for(int i = 1; i <= m; i++){
        queue<int>& ans = anss[queries[i].ID];
        Query &qi = queries[i];
        while(R < qi.R) insert(A[++R]);
        while(L > qi.L) insert(A[--L]);
        while(R > qi.R) erase(A[R--]);
        while(L < qi.L) erase(A[L++]);

        for(map<int, int>::iterator it = buf.begin(); it != buf.end(); ++it){
            if(it->second >= 2){
                ans.push(it->first);
            }
        }
    }
    for(int i = 1; i <= m; i++){
        queue<int>& ans = anss[i];
        while(!ans.empty()){
            cout << ans.front() << ' ';
            ans.pop();
        }
        cout << endl;
    }
}

在update的同时更新ans。(本题可考虑维护一个set,表示出现至少两次的数的集合)

posted @ 2017-07-22 21:14  我在地狱  阅读(206)  评论(0编辑  收藏  举报