矩形(rectangle)

矩形(rectangle)

给定 n×nn\times n 的 0101 矩形。mm 次询问,每次询问求有多少个 aa 行 bb 列的矩形满足其边上均为 11。

n,m≤1500n,m\le 1500,时限 5s5s。

首先套路性的求出 ri,jr_{i,j} 表示 (i,j)(i,j) 右边最长可以延伸的长度,di,jd_{i,j} 表示 (i,j)(i,j) 下面最长可以延伸的长度。

然后一个矩形 (i,j)(i+a−1,j+b−1)(i,j)(i+a-1,j+b-1) 合法当且仅当:

[ri,j≥b][di,j≥a][di,j+b−1≥a][ri+a−1,j≥b][r_{i,j}\ge b][d_{i,j}\ge a][d_{i,j+b-1}\ge a][r_{i+a-1,j}\ge b]

这是一个偏序问题,可以使用 bitset 解决。

不妨设 bitset Ri,j,k,Di,j,kR_{i,j,k},D_{i,j,k} 表示 [ri,j≥k],[di,j≥k][r_{i,j}\ge k],[d_{i,j}\ge k],那么根据我们精湛的 bitset 技术,可得答案需要枚举行数 ii,然后累加:

1
2
for(int j=1;j+a-1<=n;j++)
ans+=(D[j][a]&(D[j][a]>>b-1)&R[j][b]&R[j+a-1][b]).count();

这样做时间复杂度 O(n3ω)\mathcal{O}(\frac{n^3}{\omega}),空间复杂度 O(n3ω)\mathcal{O}(\frac{n^3}{\omega})。这样空间会爆掉。

考虑经典 bitset trick:离线询问。

不妨将所有询问离线下来,然后考虑扫描线,不存储 R,DR,D 的最后一维:设当前扫描线到 kk,则 Ri,j=[ri,j≥k],Di,j=[di,j≥k]R_{i,j}=[r_{i,j}\ge k],D_{i,j}=[d_{i,j}\ge k]。

那这样空间复杂度就是 O(n2ω)\mathcal{O}(\frac{n^2}{\omega}) 的了。

那么我们只要考虑记录所有询问的每一行的 bitset 是谁,然后每次一旦扫到 aa 或者 bb,可以直接将其 ∧\land 到对应行的 bitset 上。

具体代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
bitset<N>R[N],ans[N][N];
vector<int>va[N],vb[N];
vector<pii>vr[N],vd[N];
void solve(int m){
for(int i=1;i<=n;i++)va[i].clear(),vb[i].clear();
for(int i=1,a,b;i<=m;i++){
scanf("%d%d",&a,&b);
for(int j=1;j<=n;j++)ans[j][i].reset();
va[a].push_back(i),vb[b].push_back(i);
A[i]=a,B[i]=b;
}
for(int i=1;i<=n;i++)R[i].reset();
for(int i=n;i>=1;i--){
for(pii k:vr[i])
R[k.first][k.second]=1;
for(int k:vb[i])
for(int j=1;j<=n;j++)
ans[j][k]=R[j]&R[j+A[k]-1];
}
for(int i=1;i<=n;i++)R[i].reset();
for(int i=n;i>=1;i--){
for(pii k:vd[i])
R[k.first][k.second]=1;
for(int k:va[i])
for(int j=1;j<=n;j++)
ans[j][k]&=R[j]&(R[j]>>B[k]-1);
}
for(int i=1;i<=m;i++){
int cnt=0;
for(int j=1;j<=n;j++)
cnt+=ans[j][i].count();
printf("%d\n",cnt);
}
}

然后此时你就会发现一个问题:空间还是 O(n2mω)\mathcal{O}(\frac{n^2m}{\omega})。

接下来考虑经典 bitset trick:分组询问。

具体的,将询问分为 BB 组,对每一组内部分别离线询问,则空间复杂度 O(n2Bω)\mathcal{O}(\frac{n^2B}{\omega})。但这样空间复杂度会变化:O(Bn3+n2mω)\mathcal{O}(\frac{Bn^3+n^2m}{\omega})。

于是取一个 B=15B=15 即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <bits/stdc++.h>
using namespace std;
const int N=1510;
using pii=pair<int,int>;
int n,m,a[N][N],r[N][N],d[N][N],A[N],B[N];
bitset<N>R[N],ans[N][110];
vector<int>va[N],vb[N];
vector<pii>vr[N],vd[N];
void solve(int m){
for(int i=1;i<=n;i++)va[i].clear(),vb[i].clear();
for(int i=1,a,b;i<=m;i++){
scanf("%d%d",&a,&b);
for(int j=1;j<=n;j++)ans[j][i].reset();
va[a].push_back(i),vb[b].push_back(i);
A[i]=a,B[i]=b;
}
for(int i=1;i<=n;i++)R[i].reset();
for(int i=n;i>=1;i--){
for(pii k:vr[i])
R[k.first][k.second]=1;
for(int k:vb[i])
for(int j=1;j<=n;j++)
ans[j][k]=R[j]&R[j+A[k]-1];
}
for(int i=1;i<=n;i++)R[i].reset();
for(int i=n;i>=1;i--){
for(pii k:vd[i])
R[k.first][k.second]=1;
for(int k:va[i])
for(int j=1;j<=n;j++)
ans[j][k]&=R[j]&(R[j]>>B[k]-1);
}
for(int i=1;i<=m;i++){
int cnt=0;
for(int j=1;j<=n;j++)
cnt+=ans[j][i].count();
printf("%d\n",cnt);
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
scanf("%1d",&a[i][j]);
for(int i=1;i<=n;i++)for(int j=n;j>=1;j--)
if(a[i][j]==0)r[i][j]=0;
else r[i][j]=r[i][j+1]+1;
for(int j=1;j<=n;j++)for(int i=n;i>=1;i--)
if(a[i][j]==0)d[i][j]=0;
else d[i][j]=d[i+1][j]+1;
for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
vr[r[i][j]].push_back({i,j}),vd[d[i][j]].push_back({i,j});
int sum=0;
for(int i=1;i<=14;i++)sum+=(m/15),solve(m/15);
solve(m-sum);
return 0;
}