BestCoder Round #84 1003
分析:(先奉上zimpha巨官方题解)
感悟:看到题解单调队列,秒懂如何处理每个点的范围,但是题解的一句算贡献让我纠结半天
已知一个点的up,down,left,right,即上下左右的扩展范围,如何确定贡献呢
其实也很好做,把所有可能的矩形的长算出来,得到和,宽也是一样,然后乘法原理乘起来就好
#include <stdio.h> #include <iostream> #include <algorithm> #include <string.h> #include <vector> #include <math.h> #include <stack> #include <map> using namespace std; typedef long long LL; const int N = 1e3+5; const int INF = 0x3f3f3f3f; const LL mod = 1ll<<32; int a[N][N],l[N][N],r[N][N],u[N][N],d[N][N]; int T,n,m; stack<int>s; LL cal(LL x,LL y){ return (x*(x+1)/2*y%mod+y*(y-1)/2*x%mod)%mod; } int main(){ scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) for(int j=1;j<=m;++j) scanf("%d",&a[i][j]); for(int i=1;i<=n;++i){ a[i][m+1]=a[i][0]=-INF; while(!s.empty())s.pop();s.push(0); for(int j=1;j<=m;++j){ while(!s.empty()&&a[i][s.top()]>a[i][j])s.pop(); l[i][j]=s.top();s.push(j); } while(!s.empty())s.pop();s.push(m+1); for(int j=m;j;--j){ while(!s.empty()&&a[i][s.top()]>a[i][j])s.pop(); r[i][j]=s.top();s.push(j); } } for(int j=1;j<=m;++j){ a[0][j]=a[n+1][j]=INF; while(!s.empty())s.pop();s.push(0); for(int i=1;i<=n;++i){ while(!s.empty()&&a[s.top()][j]<a[i][j])s.pop(); u[i][j]=s.top();s.push(i); } while(!s.empty())s.pop();s.push(n+1); for(int i=n;i;--i){ while(!s.empty()&&a[s.top()][j]<a[i][j])s.pop(); d[i][j]=s.top();s.push(i); } } LL ret=0; for(int i=1;i<=n;++i) for(int j=1;j<=m;++j){ LL curl=j-l[i][j],curr=r[i][j]-j; LL curu=i-u[i][j],curd=d[i][j]-i; ret+=cal(curl,curr)*cal(curu,curd)%mod*a[i][j]%mod; ret%=mod; } printf("%I64d ",ret); } return 0; }