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;
}
posted @ 2025-09-09 15:06  WuMin4  阅读(15)  评论(0)    收藏  举报