【校内模拟】【19-05-25】矩阵 【矩阵前缀和+差分】

    xiaoxiao2023-11-01  141

    传送门(校内)

    题目大意

    一个有n∗m 个格子的矩阵,每个格子用“.”或“#”表示,“.”表示这个格子可以放东西,“#”则表示这个格子不能放东西。现在有一条 1∗2 大小的木棒,给定q个子矩阵,求对于给定的每个子矩阵,有多少种放木棒的方案。 q≤105, 1 ≤ n , m ≤ 500 1≤n,m≤500 1n,m500

    解析

    考场上我能切的题怕都是水题

    实际上我一开始的思路是差分,直到我打了半个小时,统计出了每一个(1,1,x,y)矩阵的答案,发现这东西不能差分……

    然后冷静下来一想:emm?这前缀和就完了啊?

    具体来讲,我们用四个数组 b [ i ] [ j ] , c [ i ] [ j ] , s u m b [ i ] [ j ] , s u m c [ i ] [ j ] b[i][j],c[i][j],sumb[i][j],sumc[i][j] b[i][j]c[i][j]sumb[i][j]sumc[i][j]

    由于摆放的方式只有横着和竖着,所以:

    b [ i ] [ j ] b[i][j] b[i][j]表示第 i i i行的前 j j j个格子横着放的方案数

    c [ i ] [ j ] c[i][j] c[i][j]表示第 j j j列的前 i i i个格子竖着放的方案数

    s u m b [ i ] [ j ] sumb[i][j] sumb[i][j]表示在矩阵 ( 1 , 1 , i , j ) (1,1,i,j) 11ij中横着放的方案数

    s u m c [ i ] [ j ] sumc[i][j] sumc[i][j]表示在矩阵 ( 1 , 1 , i , j ) (1,1,i,j) 11ij中竖着放的方案数

    考虑能不能循环利用一下,发现可以,所以可以节约空间

    不过代码里还是开了四个,实际上把sumb改成b,sumc改成c一样可以

    然后对于每个询问 ( x 1 , y 1 , x 2 , y 2 ) (x1,y1,x2,y2) x1y1x2y2

    a n s = ans= ans= ( s u m b [ x 2 ] [ y 2 ] + s u m c [ x 2 ] [ x 2 ] ) (sumb[x2][y2]+sumc[x2][x2]) sumb[x2][y2]+sumc[x2][x2] − ( s u m b [ x 2 ] [ y 1 ] + s u m c [ x 2 ] [ y 1 − 1 ] ) -(sumb[x2][y1]+sumc[x2][y1-1]) sumb[x2][y1]+sumc[x2][y11] − ( s u m b [ x 1 − 1 ] [ y 2 ] + s u m c [ x 1 ] [ y 2 ] ) -(sumb[x1-1][y2]+sumc[x1][y2]) sumb[x11][y2]+sumc[x1][y2] + ( s u m b [ x 1 − 1 ] [ y 1 ] + s u m c [ x 1 ] [ y 1 − 1 ] ) +(sumb[x1-1][y1]+sumc[x1][y1-1]) +sumb[x11][y1]+sumc[x1][y11]

    结果最后还是归结到容斥上来了~~

    上代码:

    #include<bits/stdc++.h> #define rint register int #define ivoid inline void #define iint inline int #define endll '\n' #define ll long long using namespace std; const int N=1e6+5; const int M=505; const int inf=0x3f3f3f3f; int n,m,u,v,w,x,y,z; char a[M][M]; int b[M][M],c[M][M],sum,tot,res,l,r,mx,my,num; iint rad() { int x=0,f=1;char c; while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();} while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-'0',c=getchar(); return x*f; } int main() { // freopen("matrix.in","r",stdin); // freopen("matrix.out","w",stdout); n=rad();m=rad(); for(rint i=1;i<=n;i++)scanf("%s",a[i]+1); //b 横向 c 竖向 for(rint i=1;i<=n;i++){ for(rint j=1;j<=m;j++){ b[i][j]=b[i][j-1]; c[i][j]=c[i-1][j]; if(a[i][j]=='.'&&a[i][j-1]=='.')b[i][j]++; if(a[i][j]=='.'&&a[i-1][j]=='.')c[i][j]++; } } for(rint i=1;i<=n;i++){ for(rint j=1;j<=m;j++){ b[i][j]=b[i-1][j]+b[i][j]; c[i][j]=c[i][j-1]+c[i][j]; } } int T=rad(),x1,y1,x2,y2; for(rint i=1;i<=T;i++){ x1=rad();y1=rad();x2=rad();y2=rad(); sum=0; sum+=sumb[x2][y2]+sumc[x2][y2]; sum-=sumb[x2][y1]+sumc[x2][y1-1]; sum-=sumb[x1-1][y2]+sumc[x1][y2]; sum+=sumb[x1-1][y1]+sumc[x1][y1-1]; cout<<sum<<endll; } fclose(stdin); fclose(stdout); }

    总结

    最后能想出正解是因为我想到了理想的正方形 以及他的小伙伴修筑绿化带 这种前缀和の前缀和总感觉很好玩2333~

    最新回复(0)