2 条题解

  • 3
    @ 2026-10-21 20:07:06

    矩形(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) 合法当且仅当:

    $$[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,然后累加:

    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 上。

    具体代码:

    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 即可。

    #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;
    }
    

    信息

    ID
    2812
    时间
    5000ms
    内存
    128MiB
    难度
    7
    标签
    (无)
    递交数
    41
    已通过
    4
    上传者