题解 AT2381【[AGC015C] Nuske vs Phantom Thnook】

$\text{Link}$

题意

给一个 $n\times m$ 的矩阵,有 $0$、$1$ 两种颜色,若相邻格子的颜色相同就连边,每个连通块都是树的形式,$Q$ 组询问 $\begin{bmatrix}(x_1,y_1)&\\&(x_2,y_2)\end{bmatrix}$ 这个子矩阵中有多少个连通块。

$1\le n,m\le 2000,1\le Q\le2\times10^5$

思路

这算是 $\text{AGC}$ 系列中非常简单的题了,我都能自己做出来(

首先题目问子矩阵构成的森林的树的个数,我们思考如何转化。

众所周知,树的点数 $p$ 等于边数 $e$ 加 $1$,若要将一个 $x$ 个连通块的森林连成一棵树需要 $x-1$ 条边(相当于将每颗数看为一个点,这些点再构成一棵树),所以在一个 $x$ 个连通块的森林中,$p=e+x$。

问题转化为子矩阵内点数减去边数。点数很好搞,可以直接前缀和。对于边,我们记录每个点向左、向上连的边数的前缀和,再记录每列上向左连的边数的前缀和每行上向上连的边数的前缀和,用一次二维前缀和,$2n$ 次一维前缀和即可预处理,询问时用第一个算出来的结果减去多算的即可,多算的可以简单地使用后两个算出来。

时间复杂度 $O(nm+Q)$。

代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
namespace IO{//by cyffff

}
const int N=2e3+10;
int n,m,q,edg[N][N];
bool poi[N][N];
int pr1[N][N],pr2[N][N],lin[N][N],row[N][N];
inline int point(int a1,int b1,int a2,int b2){
    return pr1[a2][b2]-pr1[a2][b1-1]-pr1[a1-1][b2]+pr1[a1-1][b1-1];
}
inline int edge(int a1,int b1,int a2,int b2){
    return pr2[a2][b2]-pr2[a2][b1-1]-pr2[a1-1][b2]+pr2[a1-1][b1-1]-lin[a2][b1]+lin[a1-1][b1]-row[a1][b2]+row[a1][b1-1];
}
int main(){
    n=read(),m=read(),q=read();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            poi[i][j]=getc()=='1';
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(poi[i][j])
                edg[i][j]=poi[i-1][j]+poi[i][j-1];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            lin[i][j]=lin[i-1][j]+(poi[i][j]&&poi[i][j-1]),
            row[i][j]=row[i][j-1]+(poi[i][j]&&poi[i-1][j]);
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            pr1[i][j]=poi[i][j]+pr1[i-1][j]+pr1[i][j-1]-pr1[i-1][j-1],
            pr2[i][j]=edg[i][j]+pr2[i-1][j]+pr2[i][j-1]-pr2[i-1][j-1];
    while(q--){
        int a1=read(),b1=read(),a2=read(),b2=read();
        write(point(a1,b1,a2,b2)-edge(a1,b1,a2,b2));
        putc('\n');
    }
    flush();
}

再见 qwq~

posted @ 2021-08-06 18:57  ffffyc  阅读(8)  评论(0)    收藏  举报  来源