【洛谷3474】[POI2008] KUP-Plot purchase(悬线法)

题目链接

  • 给定一个 (n imes n) 的矩阵和一个整数 (k),要求找出任意一个元素和在 ([k,2k]) 范围内的子矩阵,或判断不存在。
  • (1le nle2 imes10^3)(1le kle10^9)(0le a_{i,j}le2 imes10^9)

性质讨论

显然,如果子矩阵中包含大于(2k) 的元素,这个子矩阵的元素和肯定大于 (2k)

而如果我们想在一个所有元素都小于等于 (2k) 的子矩阵中寻找一个元素和在 ([k,2k]) 范围内的子矩阵,假设元素和为 (S),根据 (S) 的大小讨论:

  • (S < k):显然无解。
  • (kle Sle 2k):这个子矩阵就是答案。
  • (S>2k):不断将它拆分,则总有一个时刻会拆出两个小于等于 (2k) 的子矩阵,其中一个大于等于 (k),一个小于 (k),大于等于 (k) 且小于等于 (2k) 的那个可以作为答案。

综上,只要一个子矩阵中没有大于 (2k) 的元素且元素和大于等于 (k),就可以从中找出答案。

实际上我们只要找出所有没有大于 (2k) 的元素的极大子矩阵,如果它的元素和大于等于 (k) 就可以找出答案,否则无解。

悬线法找极大子矩阵

悬线法是在有障碍点的情况下求极大子矩阵的一个算法。

对于每个位置我们预处理出它能到的最上面的一行。

显然极大子矩阵中至少有一列顶到了它能到的最上面的一行,且它无法再向两侧扩展。

也就是说,我们可以去枚举每一行,对于这行的每个位置,利用单调栈求出选中了 从这个位置到它能到的最上面的位置(悬线)后,向左向右分别能扩展到哪一列。

一旦找到一个元素和大于等于 (k) 的子矩阵就可以去寻找答案了。

代码:(O(n^2))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define LL long long
#define N 2000
#define S(A,B,C,D) (s[C][D]-s[(A)-1][D]-s[C][(B)-1]+s[(A)-1][(B)-1])
using namespace std;
int n,k,St[N+5],L[N+5],R[N+5],a[N+5][N+5],h[N+5][N+5];LL s[N+5][N+5];
I void Solve(CI A,CI B,CI C,CI D)//在(A,B)-(C,D)这个子矩阵中找答案
{
	RI t=S(A,B,C,D);if(t<k) return;if(t<=2*k) printf("%d %d %d %d
",A,B,C,D),exit(0);
	if(A^C) {RI m=A+C>>1;return Solve(A,B,m,D),Solve(m+1,B,C,D);}RI m=B+D>>1;return Solve(A,B,C,m),Solve(A,m+1,C,D);//分割,实际上可以一行一列地删
}
int main()
{
	RI i,j;for(scanf("%d%d",&k,&n),i=1;i<=n;++i) for(j=1;j<=n;++j) scanf("%d",&a[j][i]);
	for(i=1;i<=n;++i) for(j=1;j<=n;++j) s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];//二维前缀和
	for(j=1;j<=n;++j) h[0][j]=1;for(i=1;i<=n;++i) for(j=1;j<=n;++j) h[i][j]=a[i][j]<=2*k?h[i-1][j]:i+1;//预处理每个位置能到的最上面一行
	RI T;for(i=1;i<=n;++i)//枚举每一行
	{
		for(h[i][St[T=1]=0]=n+2,j=1;j<=n;L[j]=St[T]+1,St[++T]=j++) W(h[i][St[T]]<=h[i][j]) --T;//求出每根悬线向左最多到哪一列
		for(h[i][St[T=1]=n+1]=n+2,j=n;j;R[j]=St[T]-1,St[++T]=j--) W(h[i][St[T]]<=h[i][j]) --T;//求出每根悬线向右最多到哪一列
		for(j=1;j<=n;++j) S(h[i][j],L[j],i,R[j])>=k&&(Solve(h[i][j],L[j],i,R[j]),0);//判断是否存在元素和大于k的极大子矩阵
	}return puts("NIE"),0;
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu3474.html