Maximum Subrectangle(CodeForces-1060C#513)(预处理优化暴力)

前言

打的时候开始写了 O ( n 2 l o g n ) O(n^2log_n) O(n2logn)的二分,调了半天…还是没过,然后发现直接预处理暴力就过了…

题目

传送门

题目大意:

现在你有一个长度为n的序列A,长度为m的序列B,和一个数x,然后A·B得到一个n*m的矩阵C,现在让你选择 x 1 , x 2 , y 1 , y 2 x_1,x_2,y_1,y_2 x1,x2,y1,y2满足 1 &lt; = x 1 &lt; = x 2 &lt; = n , 1 &lt; = y 1 &lt; = y 2 &lt; = m 1&lt;=x1&lt;=x2&lt;=n,1&lt;=y1&lt;=y2&lt;=m 1<=x1<=x2<=n,1<=y1<=y2<=m
∑ i = x 1 x 2 ∑ i = y 1 y 2 C i , j &lt; = x \sum_{i=x_1}^{x2}\sum_{i=y_1}^{y_2}C_{i,j}&lt;=x i=x1x2i=y1y2Ci,j<=x的条件下让矩形面积最大,求出矩形面积最大值 ( x 2 − x 1 + 1 ) ∗ ( y 2 − y 1 + 1 ) (x_2-x_1+1)*(y_2-y_1+1) (x2x1+1)(y2y1+1)
直白翻译:框出一个子矩形让矩形内权值和小于x的前提下面积最大

数据范围

1 &lt; = n , m &lt; = 2000 1&lt;=n,m&lt;=2000 1<=n,m<=2000
1 &lt; = a [ i ] , b [ j ] &lt; = 2000 ( i ∈ [ 1 , n ] , j ∈ [ 1 , m ] , ) 1&lt;=a[i],b[j]&lt;=2000(i∈[1,n],j∈[1,m],) 1<=a[i],b[j]<=2000(i[1,n],j[1,m],)

思路

温馨提示:作者会从无脑暴力到有脑暴力

O ( n 6 ) O(n^6) O(n6)

这是小白的做法…处理出C,暴力枚举要选矩形的左上角和右下角,然后扫一遍内部元素,判断是否合法,ans不断更新…

O ( n 4 ) O(n^4) O(n4)

我们其实可以发现 :
∑ i = x 1 x 2 ∑ i = y 1 y 2 C i , j = ∑ i = x 1 x 2 a [ i ] ∗ ∑ i = y 1 y 2 b [ j ] \sum_{i=x_1}^{x2}\sum_{i=y_1}^{y_2}C_{i,j}=\sum_{i=x_1}^{x2}a[i]*\sum_{i=y_1}^{y_2}b[j] i=x1x2i=y1y2Ci,j=i=x1x2a[i]i=y1y2b[j]
那么我们记 s 1 s_1 s1为a数组的前缀和, s 2 s_2 s2为b数组的前缀和,显然又有:
( s 1 [ x 2 ] − s 1 [ x 1 − 1 ] ) ∗ ( s 2 [ y 2 ] − s 2 [ y 1 − 1 ] ) &lt; = x (s_1[x_2]-s_1[x_1-1])*(s_2[y_2]-s_2[y_1-1])&lt;=x (s1[x2]s1[x11])(s2[y2]s2[y11])<=x

O ( n 3 l o g n ) O(n^3log_n) O(n3logn)

我们又可以发现当我们确定矩形的右下角和一条边长,另一条边其实可以二分找出来的如果第一条边是枚举的i则:
判断 s 1 [ i ] − s 1 [ k − 1 ] ) ∗ ( s 2 [ j ] − s 2 [ m i d − 1 ] s_1[i]-s_1[k-1])*(s_2[j]-s_2[mid-1] s1[i]s1[k1])(s2[j]s2[mid1]与x大小关系二分就可以了
图片

O ( n 2 l o g n 2 ) O(n^2log_{n^2}) O(n2logn2)

O ( n 2 ) O(n^2) O(n2) O ( m 2 ) O(m^2) O(m2)时间处理出a,b中长度为len的一段区间的最小元素和s1[len]和s2[len]
这就可以过题了,我们刚刚枚举的必须确定一个点,但我们可以换一下思路,我们枚举两条平行的边,假设我们枚举B,记 s 2 [ p ] − s 2 [ q − 1 ] = k s_2[p]-s_2[q-1]=k s2[p]s2[q1]=k,这已经是 O ( n 2 ) O(n^2) O(n2)了,那么在A中的两个端点(记为l,r)则有
s 1 [ r ] − s 1 [ l − 1 ] &lt; = x / k s_1[r]-s_1[l-1]&lt;=x/k s1[r]s1[l1]<=x/k
发现右边可以提前算出来…
那我们就可以 O ( n 2 ) O(n^2) O(n2)预处理出 s 2 [ p ] − s 2 [ q − 1 ] s_2[p]-s_2[q-1] s2[p]s2[q1]存在一个数组中,同时记录一下 p − q + 1 p-q+1 pq+1那么我们就可以二分找出答案了,
注意我们的目的要 p − q + 1 p-q+1 pq+1尽量地大,我们还要将较小的差值但较大的长度传给较大差值的数。然后照常记录答案就可以了

代码

#include<set>
#include<map>
#include<ctime>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<climits>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
int read(){
    int f=1,x=0;char s=getchar();   
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}  
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
#define MAXN 2000
#define INF 0x3f3f3f3f
#define Mod int(1e9+7)
int n,m;
struct node{
	LL val,l;
	friend bool operator < (node a,node b){return a.val<b.val;}//排序规则
}Map[MAXN*MAXN+5];
LL a[MAXN+5],b[MAXN+5],s1[MAXN+5],s2[MAXN+5];
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)
		a[i]=read(),s1[i]=s1[i-1]+a[i];
	for(int i=1;i<=m;i++)
		b[i]=read(),s2[i]=s2[i-1]+b[i];
	LL x=read(),len=0,ans=0;
	for(int i=1;i<=m;i++)
		for(int j=i;j<=m;j++)//预处理出B
			Map[len].l=j-i+1,Map[len++].val=s2[j]-s2[i-1];
	sort(Map,Map+len);
	for(int i=1;i<len;i++)
		Map[i].l=max(Map[i].l,Map[i-1].l);
	for(int i=1;i<=n;i++)
		for(int j=i;j<=n;j++){
			LL S=s1[j]-s1[i-1],sum=x/S;
			if(!sum) continue;
			LL L=0,R=len,mid;
			while(L+1<R){//二分找答案
				mid=(L+R)>>1;
				if(Map[mid].val<=sum) L=mid;
				else R=mid;
			}
			if(Map[L].val>sum) continue;
			ans=max(ans,Map[L].l*(j-i+1));
		}
	printf("%lld\n",ans);
	return 0;
}

O ( n 2 ) O(n^2) O(n2)

我们可以知道最大值的子矩形的 s 1 [ x 2 ] − s 1 [ x 1 − 1 ] s_1[x_2]-s_1[x_1-1] s1[x2]s1[x11]的值对于 x 2 − x 1 + 1 x_2-x_1+1 x2x1+1的长度必然是最小的(因为这样才是最优,否则可以选比它更小的),那么我们又可以进行一个简单的Dp
定义 f [ 2 ] [ l ] f[2][l] f[2][l]为(横竖两种不同方向上)长度为l的最小权值和
那么枚举两个方向上的 f [ 0 ] [ i ] f[0][i] f[0][i] f [ 1 ] [ j ] f[1][j] f[1][j]使它们相乘小于x时记录答案就可以了,完美的 O ( n 2 ) O(n^2) O(n2)
这里算f时我用了一个 s [ l ] [ i ] s[l][i] s[l][i]进行辅助(用前缀和之差更好)

代码

#include<set>
#include<map>
#include<ctime>
#include<queue>
#include<cmath>
#include<cstdio>
#include<vector>
#include<climits>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
int read(){
    int f=1,x=0;char s=getchar();   
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}  
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    return x*f;
}
#define MAXN 2000
#define INF 0x3f3f3f3f
#define Mod int(1e9+7)
int n,m;
LL x,s1[MAXN+5][MAXN+5],s2[MAXN+5][MAXN+5];
LL a[MAXN+5],b[MAXN+5],f1[MAXN+5],f2[MAXN+5];
int main(){
	n=read(),m=read();
	for(LL i=1;i<=n;i++)
		a[i]=read();
	for(LL i=1;i<=m;i++)
		b[i]=read();
	x=read();
	memset(f1,0x3f,sizeof(f1));
	memset(f2,0x3f,sizeof(f2));
	for(int l=1;l<=n;l++)
		for(int i=j;i<=n;i++)
			s1[l][i]=s1[l-1][i-1]+a[i];
	for(int l=1;l<=m;l++)
		for(int i=j;i<=m;i++)
			s2[l][i]=s2[l-1][i-1]+b[i];
	for(int l=1;l<=n;l++)
		for(int i=l;i<=n;i++)
			f1[l]=min(f1[l],s1[l][i]);
	for(int l=1;l<=m;l++)
		for(int i=l;i<=m;i++)
			f2[l]=min(f2[l],s2[l][i]);
	LL ans=0;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			if(f1[i]*f2[j]<=x)
				ans=max(ans,1ll*i*j);
	printf("%lld\n",ans);
}

题外话

O ( n 2 l o g n 2 ) O(n^2log_{n^2}) O(n2logn2)时这里不知怎么让长度为第二关键字就会炸裂TLE,CF第三组是2000*2000,所有数都是2000…

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值