1. 程式人生 > >Maximum Subrectangle(CodeForces-1060C#513)(預處理優化暴力)

Maximum Subrectangle(CodeForces-1060C#513)(預處理優化暴力)

前言

打的時候開始寫了O(n2logn)O(n^2log_n)的二分,調了半天…還是沒過,然後發現直接預處理暴力就過了…

題目

傳送門

題目大意:

現在你有一個長度為n的序列A,長度為m的序列B,和一個數x,然後A·B得到一個n*m的矩陣C,現在讓你選擇x1,x2,y1,y2x_1,x_2,y_1,y_2滿足1<=x1<=x2<=n,1<=y1<=y2<=m1<=x1<=x2<=n,1<=y1<=y2<=m

i=x1x2i=y1y2Ci,j<=x\sum_{i=x_1}^{x2}\sum_{i=y_1}^{y_2}C_{i,j}<=x的條件下讓矩形面積最大,求出矩形面積最大值(x2x1+1)(y2y1+1)(x_2-x_1+1)*(y_2-y_1+1) 直白翻譯:框出一個子矩形讓矩形內權值和小於x的前提下面積最大

資料範圍

1<=n,m<=2000

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

思路

溫馨提示:作者會從無腦暴力到有腦暴力

O(n6)O(n^6)

這是小白的做法…處理出C,暴力列舉要選矩形的左上角和右下角,然後掃一遍內部元素,判斷是否合法,ans不斷更新…

O(n4)O(n^4)

我們其實可以發現 : i=x

1x2i=y1y2Ci,j=i=x1x2a[i]i=y1y2b[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] 那麼我們記s1s_1為a陣列的字首和,s2s_2為b陣列的字首和,顯然又有: (s1[x2]s1[x11])(s2[y2]s2[y11])<=x(s_1[x_2]-s_1[x_1-1])*(s_2[y_2]-s_2[y_1-1])<=x

O(n3logn)O(n^3log_n)

我們又可以發現當我們確定矩形的右下角和一條邊長,另一條邊其實可以二分找出來的如果第一條邊是列舉的i則: 判斷s1[i]s1[k1])(s2[j]s2[mid1]s_1[i]-s_1[k-1])*(s_2[j]-s_2[mid-1]與x大小關係二分就可以了 圖片

O(n2logn2)O(n^2log_{n^2})

這就可以過題了,我們剛剛列舉的必須確定一個點,但我們可以換一下思路,我們列舉兩條平行的邊,假設我們列舉B,記s2[p]s2[q1]=ks_2[p]-s_2[q-1]=k,這已經是O(n2)O(n^2)了,那麼在A中的兩個端點(記為l,r)則有 s1[r]s1[l1]<=x/ks_1[r]-s_1[l-1]<=x/k 發現右邊可以提前算出來… 那我們就可以O(n2)O(n^2)預處理出s2[p]s2[q1]s_2[p]-s_2[q-1]存在一個數組中,同時記錄一下pq+1p-q+1那麼我們就可以二分找出答案了, 注意我們的目的要pq+1p-q+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("%l