qoj1857 PlayerUnknown's Battlegrounds
题意
Rikka吃鸡开挂
给出一个 \(n\times m\) 的网格,所有格子上的数字构成一个 \(n\times m\) 的排列。
对 \(k=1,2,\cdots,n\times m\) 求出有多少不同子网格的最小值为 \(k\)。
\(n,m\le 300\)。
思路
设子网格的上下左右边界分别为 \(x_l,x_r,y_l,y_r\),最小值为 \(mn_{x_l,x_r,y_l,y_r}\)。
首先考虑固定 \(x_l,x_r\),可以直接枚举,此时显然有 \(mn_{x_l,x_r,j,j}=\min_{i=x_l}^{x_r} a_{i,j}\)。
令 \(b_j=\min_{i=x_l}^{x_r} a_{i,j}\),则对于位置 \(j\) 所有满足 \(\min_{i=y_l}^{y_r} b_i = b_j\) 的 \(y_l,y_r\) 都可以作为 \(b_j\) 的答案。
于是问题就变为了找出 \({y_l}_{min}\) 和 \({y_r}_{max}\),显然单调栈模板,给 \(b_j\) 贡献的方案数即为 \((j-{y_l}_{min}+1)\times ({y_r}_{max}-j+1)\)。
\(b_j\) 可以动态更新,时间复杂度 \(O(n^2m)\),可以通过。
代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int n,m,a[305][305],b[305],l[305],r[305],stk[305],top,ans[90005];
signed main() {
ios::sync_with_stdio(false);
cin.tie(nullptr),cout.tie(nullptr);
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)
b[j]=a[i][j];
for(int ii=i;ii<=n;ii++){
for(int j=1;j<=m;j++)
b[j]=min(b[j],a[ii][j]);
top=0;
for(int j=m;j>=1;j--){
while(b[j]<=b[stk[top]]) top--;
r[j]=(!stk[top]?m+1:stk[top]),stk[++top]=j;
}
top=0;
for(int j=1;j<=m;j++){
while(b[j]<=b[stk[top]]) top--;
l[j]=stk[top],stk[++top]=j;
}
for(int j=1;j<=m;j++){
ans[b[j]]+=(r[j]-j)*(j-l[j]);
}
}
}
for(int i=1;i<=n*m;i++)
cout<<ans[i]<<endl;
return 0;
}

浙公网安备 33010602011771号