题面:去一个神奇的网页:https://www.cnblogs.com/Juve/articles/11352426.html
听说打O(nmq)有70
但是显然博主只有50分
考点:前缀和的综合应用
标算为:对于不包含环的图,连通块数目=点数-边数,所以利用二维前缀和进行预处理,O(1)求出矩形区域内的边数和点数.
很好写的70分算法:对每组询问都暴力求连通块数目的复杂度为O(NMQ),可以通过前7个测试点.出题人认为,在考场上,一个水平中等的选手最佳的策略是采用这个70分算法以留出时间思考第三题.
第3,4,5,6个测试点的其他做法:利用和标算同样的思路,但不需要二维前缀和,求解时较简单.
第8,9个测试点的目的是照顾常数过大的选手和数组开小的选手.
本题实际上是AtCoder Grand Contest 015的C题,可能会有一些选手因为做过原题而在本题快速得到高分,但估计不会很多
Liu_runda的官方题解
其实你手玩一下样例就会发现,一个矩形内联通块的数目等于矩形内部所有黑点个数减去所有黑点之间连的边数
维护三个前缀和,分别存黑点个数,行上的边数,列上的边数,这样就能O(1)查询
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<stack> #include<queue> #define re register #define MAXN 2005 using namespace std; int n,m,q,mapa[MAXN][MAXN]; int sum[MAXN][MAXN],sumh[MAXN][MAXN],suml[MAXN][MAXN]; int dx[2]={-1,0},dy[2]={0,-1},h[MAXN][MAXN],l[MAXN][MAXN]; char ch[MAXN]; signed main(){ scanf("%d%d%d",&n,&m,&q); for(int i=1;i<=n;i++){ scanf("%s",ch+1); for(int j=1;j<=m;j++){ mapa[i][j]=ch[j]-'0'; sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+mapa[i][j];//黑点总数前缀和 } } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ if(mapa[i][j]==0) continue; for(int k=0;k<=1;k++){ int p=i+dx[k],q=j+dy[k]; if(mapa[p][q]==0) continue; if(k==0) h[p][q]++; else l[p][q]++; } } } for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ sumh[i][j]=sumh[i-1][j]+sumh[i][j-1]-sumh[i-1][j-1]+h[i][j]; suml[i][j]=suml[i-1][j]+suml[i][j-1]-suml[i-1][j-1]+l[i][j];//内部边数的前缀和(行与列) } } for(int i=1,sx,sy,ex,ey;i<=q;i++){ scanf("%d%d%d%d",&sx,&sy,&ex,&ey); int res1=sum[ex][ey]-sum[sx-1][ey]-sum[ex][sy-1]+sum[sx-1][sy-1]; int res2=suml[ex][ey-1]-suml[sx-1][ey-1]-suml[ex][sy-1]+suml[sx-1][sy-1]; int res3=sumh[ex-1][ey]-sumh[sx-1][ey]-sumh[ex-1][sy-1]+sumh[sx-1][sy-1]; int ans=res1-res2-res3; printf("%d\n",ans); } return 0; }