题目大意:给定一段只包含-1,1的序列,每次询问区间内满足区间和为0的最长子区间长度;
题目解析:分块真的是个很神奇的东西。朴素做法是每次询问搞出前缀和,然后二分,复杂度为M*N*logn,考虑分块的做法,其实就是预处理出每个块的答案,然后询问的时候只需要考虑不在最大块里面的元素即可,总时间复杂度降为M*N^0.5*logn;
AC代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e4+10;
const int maxm = 1e3;
const int del= 5e4+10;
int ans[maxm][maxm];
vector<int>sum[maxn<<1];
int n,m;
int a[maxn];
int vis[maxn<<1];
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=0;i<maxn<<1;i++)
sum[i].clear();
a[0] = del;
sum[a[0]].push_back(0);
for(int i=1;i<=n;i++)
{
int t;
scanf("%d",&t);
a[i]=a[i-1]+t;
sum[a[i]].push_back(i);
}
int unit = sqrt(n);
int siz = (n+unit)/unit;
for(int i=0;i<=n;i+=unit)
{
memset(vis,-1,sizeof(vis));
int t = 0;
for(int j=i;j<=n;j++)
{
if(j%unit==0) ans[i/unit][j/unit]=t;
if(vis[a[j]]==-1) vis[a[j]]=j;
else t = max(t,j-vis[a[j]]);
}
ans[i/unit][siz]=t;
}
while(m--)
{
int l,r;
scanf("%d%d",&l,&r);
l--;
int x = (l+unit)/unit*unit;
int y = r/unit*unit;
int res = ans[(l+unit)/unit][r/unit];
//cout<<res<<endl;
for(int i=l;i<x;i++)
{
int pos = lower_bound(sum[a[i]].begin(),sum[a[i]].end(),r)-sum[a[i]].begin()-1;
if(pos==-1) continue;
res = max(res,sum[a[i]][pos]-i);
}
for(int i=y;i<=r;i++)
{
int pos = lower_bound(sum[a[i]].begin(),sum[a[i]].end(),l)-sum[a[i]].begin();
if(pos==(int)sum[a[i]].size()) continue;
res = max(res,i-sum[a[i]][pos]);
}
printf("%d\n",res);
}
}
return 0;
}