【二维前缀和】

https://nanti.jisuanke.com/t/31434

直接搬题解:

https://www.cnblogs.com/dilthey/p/9757781.html

考虑普通的 dp[i][j]=a=L+1ib=D+1j((a==i&&b==j)?0:dp[a][b])dp[i][j]=∑a=L+1i∑b=D+1j((a==i&&b==j)?0:dp[a][b]),显然就是一整个大方块去掉一小格求和,

其中,D=max(0,ik1)D=max(0,i−k−1) 为下开边界,L=max(0,jk1)L=max(0,j−k−1) 为左开边界,

如果我们老老实实的纯暴力DP,显然就是 O(WHK2)O(WHK2) 的时间复杂度,能过有鬼……

需要使用前缀和优化,不妨假设 sum[i][j]=a=1ib=1jdp[a][b]sum[i][j]=∑a=1i∑b=1jdp[a][b],

那么,显然有状态转移方程 dp[i][j]=(sum[i1][j]+sum[i][j1]sum[i1][j1])(sum[D][j]+sum[i][L]sum[D][L])dp[i][j]=(sum[i−1][j]+sum[i][j−1]−sum[i−1][j−1])−(sum[D][j]+sum[i][L]−sum[D][L]),

进而显然有状态转移方程:

sum[i][j]=(sum[i1][j]+sum[i][j1]sum[i1][j1])+dp[i][j]=2(sum[i1][j]+sum[i][j1]sum[i1][j1])(sum[D][j]+sum[i][L]sum[D][L])sum[i][j]=(sum[i−1][j]+sum[i][j−1]−sum[i−1][j−1])+dp[i][j]=2(sum[i−1][j]+sum[i][j−1]−sum[i−1][j−1])−(sum[D][j]+sum[i][L]−sum[D][L])

 

最后,易知答案为 dp[h][w]=sum[h][w](sum[h1][j]+sum[i][w1]sum[h1][w1])dp[h][w]=sum[h][w]−(sum[h−1][j]+sum[i][w−1]−sum[h−1][w−1])。

 

AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const ll MOD=998244353;
const int maxn=2000+10;

int w,h,k;
ll sum[maxn][maxn];

int main()
{
scanf("%d%d%d",&w,&h,&k);

memset(sum,0,sizeof(sum));
sum[1][1]=1;
for(int i=1;i<=h;i++)
{
for(int j=1;j<=w;j++)
{
if(i==1&&j==1) continue;
int L=max(0,i-k-1),D=max(0,j-k-1);
sum[i][j]=2*(sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1])-(sum[L][j]+sum[i][D]-sum[L][D]);
while(sum[i][j]<0) sum[i][j]+=MOD; sum[i][j]%=MOD;
}
}
ll ans=sum[h][w]-sum[h-1][w]-sum[h][w-1]+sum[h-1][w-1];
while(ans<0) ans+=MOD; ans%=MOD;
printf("%lld",ans);
}

zquoj 25001

题意:图会给一个正方形,然后接下来给你一个边长的倾斜45度角的正方形,这个正方形能罩到的元素和最大为多少。

直接旋转矩阵45度角然后直接前缀和求就行了

#include<bits/stdc++.h>
#define numm ch-48
using namespace std;
template <typename T>
void read(T &res) {
bool flag=false;char ch;
while(!isdigit(ch=getchar())) (ch=='-')&&(flag=true);
for(res=numm;isdigit(ch=getchar());res=(res<<1)+(res<<3)+numm);
flag&&(res=-res);
}
template <typename T>
void write(T x) {
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10+'0');
}
const int maxn=405;
int a[maxn][maxn];
int b[maxn*2][maxn*2];
int main()
{
int n,m,k,x,y,xl,yl,xr,yr;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&a[i][j]);
m=n*2-1;//新矩阵的大小
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
b[i+j-1][n-i+j]=a[i][j];//把矩阵旋转过来
for(int i=1;i<=m;i++){
for(int j=1;j<=m;j++){
printf("%d ",b[i][j]);
}
printf("\n");
}
for(int i=1;i<=m;i++)
for(int j=1;j<=m;j++)
b[i][j]+=b[i][j-1];//前缀和
for(int j=1;j<=m;j++)
for(int i=1;i<=m;i++)
b[j][i]+=b[j-1][i];//前缀和
int ans=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
x=i+j-1,y=n-i+j;//求旋转后的坐标
xl=x-k,yl=y-k,xr=x+k,yr=y+k;//求左上角、右下角的坐标
if(xl<1) xl=1;
if(yl<1) yl=1;
if(xr>m) xr=m;
if(yr>m) yr=m;//超出矩阵外的部分要去掉
ans=max(ans,b[xr][yr]-b[xr][yl-1]-b[xl-1][yr]+b[xl-1][yl-1]);//更新答案
}
printf("%d\n",ans);
return 0;
}

posted on 2019-09-21 13:31  师姐的迷弟  阅读(221)  评论(0编辑  收藏  举报

导航