前缀和这种小技巧noip很容易就考到了,例如11年的聪明的质监员这道题二分但是用到了前缀和优化。前缀和可以优化一下时间复杂度。
首先是一维的前缀和,a[i]+=a[i-1];这样很简单使用的时候只要直接a[r]-a[l-1]就可以吧r到l直接的累加全部搞出来优化一重循环0.0;
下面是10.21考的普及组的一道题,一眼看出是个二维的前缀和,但下手的时候由于时间不充足还是选择打了暴力结果GG。
范围n,m<=300,显然二维的前缀和只要数出0和1的个数即可,不会啊~~~。
这个要怎么求呢?很显然当前的值a[i][j]=a[i][j]+a[i-1][j]+a[i][j-1]-a[i-1][j-1]即可这样前缀和就解决了,但是如何调用也是一个问题。
当前是要求每个小矩阵的0和1所以当前的点是向外延伸k的长度的所以可设当前点为i,j矩阵右下方的端点为w=i+k-1,y=j+k-1(注意细节),那么只要求出来从w,y到i,j直接的0和1的个数即可。自己画个图易知当前前缀和=a[w][y]-a[w][j-1]+a[i-1][y]+a[i-1][j-1].这样问题就被完美的解决了,这道题有很多的细节,码力不好的话很容易就wa掉。。。见代码:
#include<iostream> #include<cmath> #include<cstdio> #include<cstring> #include<string> #include<ctime> #include<vector> #include<map> #include<queue> #include<iomanip> #include<stack> #include<algorithm> using namespace std; inline long long read() { long long x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } const long long maxn=1000; long long n,m; long long a[maxn][maxn],ans=0,ans1=0,tot=0,s; struct bwy{long long x,y;}b[maxn][maxn]; int main() { //freopen("paint.in","r",stdin); //freopen("paint.out","w",stdout); //freopen("1.in","r",stdin); n=read();m=read();s=min(n,m); for(long long i=1;i<=n;i++) { for(long long j=1;j<=m;j++) { a[i][j]=read(); if(a[i][j]==1) { b[i][j].x++; b[i][j].x+=b[i][j-1].x; b[i][j].x+=b[i-1][j].x-b[i-1][j-1].x; b[i][j].y+=b[i][j-1].y; b[i][j].y+=b[i-1][j].y-b[i-1][j-1].y; } else { b[i][j].y++; b[i][j].x+=b[i][j-1].x; b[i][j].x+=b[i-1][j].x-b[i-1][j-1].x; b[i][j].y+=b[i][j-1].y; b[i][j].y+=b[i-1][j].y-b[i-1][j-1].y; } } } //for(long long i=1;i<=n;i++){for(long long j=1;j<=m;j++){printf("%d ",b[i][j].x);}printf(" ");} for(long long k=2;k<=s;k++) for(long long i=1;i<=n-k+1;i++) { for(long long j=1;j<=m-k+1;j++) { long long w=i+k-1,y=j+k-1; if(abs((b[w][y].x+b[i-1][j-1].x-b[w][j-1].x-b[i-1][y].x)-(b[w][y].y+b[i-1][j-1].y-b[w][j-1].y-b[i-1][y].y))<=1) tot++; } } printf("%lld ",tot); return 0; }
此其所挟持者甚大,而其志甚远也。