HDU 4638 Group(莫队)题解

题意:n个数,每个数有一个值,每次询问一个区间,问你这个区间能分成连续的几段(比如7 1 2 8 就是两端 1 2 和 7 8)

思路:莫队。因为L、R移动顺序wa了20发...问了一下别人,都是先扩大范围,再缩小...以后就这样写吧...

代码:

#include<cmath>
#include<cstdio>
#include<vector>
#include<cstring>
#include <iostream>
#include<algorithm>
using namespace std;
const int maxn = 100000 + 10;
int vis[maxn], arr[maxn], ans[maxn];
int T, n, m, ret;
struct node{
    int l, r;
    int pos, id;
    bool operator < (const node &x) const{
        if(pos == x.pos) return r < x.r;
        return pos < x.pos;
    }
}p[maxn];
void add(int x){
    vis[x] = 1;
    if(vis[x - 1] && vis[x + 1]) ret--;
    else if(!vis[x - 1] && !vis[x + 1]) ret++;
}
void del(int x){
    vis[x] = 0;
    if(vis[x - 1] && vis[x + 1]) ret++;
    else if(!vis[x - 1] && !vis[x + 1]) ret--;
}
void solve(){
    memset(vis, 0 ,sizeof(vis));
    int L = 1, R = 0;
    ret = 0;
    for(int i = 0; i < m; i++){
        int l = p[i].l, r = p[i].r;
        if(r < L || l > R){
            ret = 0;
            for(int i = L; i <= R; i++)
                vis[arr[i]] = 0;
            for(int i = l; i <= r; i++)
                add(arr[i]);
            L = l, R = r;
        }
        while(L > l){
            L--;
            add(arr[L]);
        }
        while(R < r){
            R++;
            add(arr[R]);
        }
        while(L < l){
            del(arr[L]);
            L++;
        }
        while(R > r){
            del(arr[R]);
            R--;
        }
        ans[p[i].id] = ret;
    }
}
int main(){
    scanf("%d", &T);
    while(T--){
        scanf("%d%d", &n, &m);
        int block = sqrt(n * 1.0);
        for(int i = 1; i <= n; i++)
            scanf("%d", &arr[i]);
        for(int i = 0; i < m; i++){
            scanf("%d%d", &p[i].l, &p[i].r);
            p[i].id = i;
            p[i].pos = p[i].l / block;
        }
        sort(p, p + m);
        solve();
        for(int i = 0; i < m; i++){
            printf("%d\n", ans[i]);
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/KirinSB/p/10582224.html