SPOJ-ZQUERY(分块)

来源:互联网 发布:智商算法 编辑:程序博客网 时间:2024/06/07 03:36

题意:有一个由1和-1组成的数列,现给M个询问(L,R),求区间(L,R)中和为0的最长子串长。

分块的思想,真的很神奇。

先考虑一个普通的询问,求一遍前缀和,记录下所有每个前缀和的所有下标,遍历一次,二分得结果,复杂度为nlgn

如果预处理出一个区间,那么对于所有包含这个区间的询问,复杂度为两区间之差乘个log。那么,当预处理好的元区间足够小时,可以有很大的优化。设元区间大小为k,那么预处理的时间为n2/k,询问复杂度为Mklgn,这里我取k=n

#include<bits/stdc++.h>using namespace std;const int N = 6e5;const int M = sqrt(N);const int d = 5e4 + 10;int n, m, x, y;int a[N];int unit, siz, ans[M][M];vector<int> adds[N];int vis[N];int temp;int main(){    while(~scanf("%d%d", &n, &m)){        for(int i = 0; i <= d * 2; i++)            adds[i].clear();        a[0] = d;        adds[d].push_back(0);        for(int i = 1; i <= n; i++){            scanf("%d", &a[i]);            a[i] += a[i - 1];            adds[a[i]].push_back(i);        }        unit = sqrt(n);        siz = (n + unit) / unit;        for(int i = 0; i <= n; i += unit){            memset(vis, -1, sizeof(vis));            temp = 0;            for(int j = i; j <= n; j++){                if(j % unit == 0) ans[i / unit][j / unit] = temp;//i到j - 1的解                if(vis[a[j]] == -1) vis[a[j]] = j;                else temp = max(temp, j - vis[a[j]]);            }            ans[i / unit][siz] = temp;        }        int res;        while(m--){            scanf("%d%d", &x, &y);//所求应是x-1到y的解            --x;            int l = (x + unit) / unit * unit;            int r = y / unit * unit;            res = ans[l / unit][r / unit];            for(int i = x; i < l; i++){                int pos = lower_bound(adds[a[i]].begin(), adds[a[i]].end(), y) - adds[a[i]].begin() - 1;                if(pos < 0) continue;                res = max(res, adds[a[i]][pos] - i);            }            for(int i = y; i >= r; i--){                int pos = lower_bound(adds[a[i]].begin(), adds[a[i]].end(), x) - adds[a[i]].begin();                if(pos == adds[a[i]].size()) continue;                res = max(res, i - adds[a[i]][pos]);            }            printf("%d\n", res);        }    }    return 0;}
原创粉丝点击