前缀和

前缀和这种小技巧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("\n");}
    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\n",tot);
    return    0;
}
View Code

此其所挟持者甚大,而其志甚远也。

posted @ 2018-10-22 20:11  chdy  阅读(395)  评论(0编辑  收藏  举报