前言
打的时候开始写了 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
<
=
x
1
<
=
x
2
<
=
n
,
1
<
=
y
1
<
=
y
2
<
=
m
1<=x1<=x2<=n,1<=y1<=y2<=m
1<=x1<=x2<=n,1<=y1<=y2<=m
∑
i
=
x
1
x
2
∑
i
=
y
1
y
2
C
i
,
j
<
=
x
\sum_{i=x_1}^{x2}\sum_{i=y_1}^{y_2}C_{i,j}<=x
∑i=x1x2∑i=y1y2Ci,j<=x的条件下让矩形面积最大,求出矩形面积最大值
(
x
2
−
x
1
+
1
)
∗
(
y
2
−
y
1
+
1
)
(x_2-x_1+1)*(y_2-y_1+1)
(x2−x1+1)∗(y2−y1+1)
直白翻译:框出一个子矩形让矩形内权值和小于x的前提下面积最大
数据范围
1
<
=
n
,
m
<
=
2000
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],)
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=x1x2∑i=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
]
)
<
=
x
(s_1[x_2]-s_1[x_1-1])*(s_2[y_2]-s_2[y_1-1])<=x
(s1[x2]−s1[x1−1])∗(s2[y2]−s2[y1−1])<=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[k−1])∗(s2[j]−s2[mid−1]与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[q−1]=k,这已经是
O
(
n
2
)
O(n^2)
O(n2)了,那么在A中的两个端点(记为l,r)则有
s
1
[
r
]
−
s
1
[
l
−
1
]
<
=
x
/
k
s_1[r]-s_1[l-1]<=x/k
s1[r]−s1[l−1]<=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[q−1]存在一个数组中,同时记录一下
p
−
q
+
1
p-q+1
p−q+1那么我们就可以二分找出答案了,
注意我们的目的要
p
−
q
+
1
p-q+1
p−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("%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[x1−1]的值对于
x
2
−
x
1
+
1
x_2-x_1+1
x2−x1+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…