【JZOJ5410】【NOIP2017提高A组集训10.22】小型耀斑

Description

Uthuso 的核反应失控了,她在地灵殿释放了几颗大核弹.地灵殿可以看做一个大小为n*m 的矩阵.一颗大小为k 的核弹,对于任意一个与爆炸中心曼哈顿距离小于k 的地区,会造成(k-(该地区到爆炸中心曼哈顿距离))*(该地区的价值)的损失.现在,地灵殿方面想统计一下每颗核弹造成的损失,请你来帮忙计算.

Data Constraint

对于30%的数据,满足n<=300,m<=300,Q<=300
对于60%的数据,满足n<=300,m<=300
对于100%的数据,满足1<=n<=2000,1<=m<=2000,1<=Q<=200000,1<=k<=min(x,y,n-x+1,m-y+1),1<=每个区域的价值<=1000000

Solution

这里写图片描述
这样的询问比较常见,但我只想到把正方形倒过来。还是不会做。对于这样的一个询问,我们可以维护一个点f[i][j]表示两条过(i,j)的斜率为1和-1的直线所包括的左边的区域之和。像这样f[3][5]这里写图片描述
那么我们现在对于
系数至少为1的数之和,我们可以用f[3][5]-f[1][2]-f[5][2]+f[3][0]。
系数至少为2的数之和,我们可以用f[3][4]-f[2][2]-f[4][2]+f[3][1]-以(1,2)为端点的斜率为-1的线段-以(5,2)为端点的斜率为1的线段。
系数至少为3的数之和,我们可以用f[3][3]-f[3][2]-f[3][2]+f[3][2]-以(2,2)为端点的斜率为-1的线段-以(4,2)为端点的斜率为1的线段。
我们发现加的f值的横坐标都为x,减的f的纵坐标都为y-1,为端点的线段的端点的纵坐标也同为y-1,所以维护一下前缀和即可。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const ll maxn=2e3+5;
ll a[maxn][maxn],f[maxn][maxn],g[maxn][maxn],l[maxn][maxn],r[maxn][maxn];
ll n,m,i,t,j,k,x,y,z,ans,xx,yy;
ll get(){
    char ch=getchar();ll x=0;
    while (ch<48 || ch>57) ch=getchar();
    while (ch>=48 && ch<=57) x=x*10+ch-48,ch=getchar();
    return x;
}
int main(){
    freopen("flare.in","r",stdin);freopen("flare.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    for (i=1;i<=n;i++)
        for (j=1;j<=m;j++)
            a[i][j]=get();
    for (j=1;j<=m;j++){
        g[0][j]=f[0][j]=f[1][j-1];
        for (i=1;i<=n+1;i++)l[i][j]=l[i-1][j-1]+a[i][j];
        for (i=n;i>=0;i--)r[i][j]=r[i+1][j-1]+a[i][j];
        for (i=1;i<=n;i++)f[i][j]=f[i][j-1]+l[i][j]+r[i][j]-a[i][j],g[i][j]=g[i-1][j]+f[i][j];
        f[n+1][j]=f[n][j-1];g[n+1][j]=g[n][j]+f[n+1][j];
    }
    for (i=1;i<=n+1;i++)
        for (j=1;j<=m;j++)
            f[i][j]+=f[i][j-1],l[i][j]+=l[i-1][j];
    for (i=n;i>=0;i--)
        for (j=1;j<=m;j++)r[i][j]+=r[i+1][j];
    scanf("%lld",&m);
    while (m--){
        scanf("%lld%lld%lld",&x,&y,&z);
        if (z>1)ans=f[x][y+z-1]-f[x][y-1]+f[x][y-2]-f[x][y-z-1];
        else ans=f[x][y]-f[x][y-1];
        ans=ans-(g[x+z-1][y-1]-g[x-z][y-1])-(l[x-1][y-1]-l[x-z-1][y-1])-(r[x+1][y-1]-r[x+z+1][y-1]);
        printf("%lld\n",ans);
    }
}
发布了257 篇原创文章 · 获赞 451 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/crybymyself/article/details/78325313