P8783 题解
题目大意
给定一个
解题思路
要想确定一个矩阵,只需要确定其左上角的点
枚举左上角的点和右下角的点,再扫描
时间复杂度
与
使用二维前缀和,用
所以矩阵该矩阵的元素之和就可以用
时间复杂度
- 满分做法:
考虑如何把复杂度压到三次方的级别。
我们可以假想有水平的两条横线,切出了中间一块区域。用
枚举出两条横线之后,就可以把中间的每一列数给当成一个整体,例如上图
我们只需要求出在这个序列中,有几个子序列的元素之和小于等于
那么现在我们还有
- 若
sum(l,r)\le k ,且l+1\le r ,则sum(l+1,r)\le k 。 - 若
sum(l,r)>k ,且r+1\le n ,则sum(l,r+1)>k 。
在遍历
举个例子:有一个序列
-
l=1,r=1,sum=1,ans=0+(1-1+1)=1 -
l=1,r=2,sum=4,ans=1+(2-1+1)=3
批注:因为
-
l=1,r=3,sum=8,ans=3+(3-1+1)=6 -
l=1,r=4,sum=11
批注:此时考虑移动
-
l=2,r=4,sum=10,ans=6+(4-2+1)=9
此时
参考代码与总结
#include<bits/stdc++.h>
using namespace std;
long long n,m,k,a[510][510],sum[510][510],b[510];
long long l,r,now,ans;
int main(){
cin>>n>>m>>k;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>a[i][j];
for(int j=1;j<=m;j++)
for(int i=1;i<=n;i++)
sum[i][j]=sum[i-1][j]+a[i][j];
for(int x=1;x<=n;x++)
for(int y=x;y<=n;y++){
// cout<<x<<" "<<y<<":"<<endl;
for(int j=1;j<=m;j++)
b[j]=sum[y][j]-sum[x-1][j];
//双指针
l=1;r=1;now=0;
for(r=1;r<=m;r++){
now+=b[r];
if(now<=k){
// cout<<l<<" "<<r<<" "<<now<<endl;
ans+=r-l+1;
}
else{
while(now>k){
now-=b[l];
l++;
}
ans+=r-l+1;
}
}
// cout<<"-------"<<endl;
}
cout<<ans<<endl;
return 0;
}
总结:该题的解题方法:
- “降维打击” 法:把二维问题转化为一维。
- 双指针法:把
O(n^2) 的复杂度,通过单调性等优秀性质,转化为O(n) 。