CF513C Maximum Subrectangle

题目

传送门

题目大意

给出分别有 N , M N,M N,M个元素的序列 a , b a,b a,b,定义一个乘积矩阵,其中的元素 c i , j = a i × b j c_{i,j}=a_i\times b_j ci,j=ai×bj,找到它的一个最大面积子矩阵,使得这个子矩阵的元素之和不大于给定的 X X X。所有数都是正数, N , M ≤ 2000 N,M\leq 2000 N,M2000

思路

比赛的时候第一次 15 15 15分钟内切掉前 2 2 2题,剧烈膨胀,结果大脑抽筋死在第 3 3 3题。
设这个矩阵左上角为 c i 1 , j 1 c_{i_1,j_1} ci1,j1,右下角为 c i 2 , j 2 c_{i_2,j_2} ci2,j2,那么它的元素之和为: ∑ i = i 1 i 2 ∑ j = j 1 j 2 a i × b j \sum\limits_{i=i_1}^{i_2}\sum\limits_{j=j_1}^{j_2}a_i\times b_j i=i1i2j=j1j2ai×bj
化简一下得: ∑ i = i 1 i 2 a i ∑ j = j 1 j 2 b j \sum\limits_{i=i_1}^{i_2}a_i\sum\limits_{j=j_1}^{j_2}b_j i=i1i2aij=j1j2bj
记录一下 a , b a,b a,b的前缀和记为 s u m a , s u m b sum_a,sum_b suma,sumb,于是得到: ( s u m a [ i 2 ] − s u m a [ i 1 − 1 ] ) × ( s u m b [ j 2 ] − s u m b [ j 2 − 1 ] ) ≤ X (sum_a[i_2]-sum_a[i_1-1])\times(sum_b[j_2]-sum_b[j_2-1])\leq X (suma[i2]suma[i11])×(sumb[j2]sumb[j21])X
相当于是从 a , b a,b a,b中分别找一段元素之和乘起来不大于 X X X


以上内容是比赛时想到的。
然后先想了个 O ( l o g N l o g M ⋅ N M ) O(logNlogM·NM) O(logNlogMNM),写着写着发现是错的。
然后想了个用set乱搞 O ( ( l o g N + l o g M ) N M ) O((logN+logM)NM) O((logN+logM)NM)的,交上去只过了样例,WA了三次后发现思路还是错的。

我直到比赛结束都觉得是个二分搜索。
于是我颓废了。


接下来是正解。
可以用 O ( N 2 ) O(N^2) O(N2) O ( M 2 ) O(M^2) O(M2)时间处理出 a , b a,b a,b长度为len的一段区间的最小元素和 S a [ l e n ] , S b [ l e n ] S_a[len],S_b[len] Sa[len],Sb[len]

然后再用 O ( N M ) O(NM) O(NM)枚举从 a a a中选长度为 i i i b b b中选长度为 j j j的一段,它们乘积之和最小为 S a [ i ] × S b [ j ] S_a[i]\times S_b[j] Sa[i]×Sb[j],如果这个值不大于 X X X,说明可以,就更新答案。

我突然觉得自己不够贪心啊= =

代码

早上起来赶的,写得丑不要介意。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

#define MAXN 2000
#define LL long long
int N[2],X,Ans;
LL Sum[2][MAXN+5],MinSum[3][MAXN+5];

int main(){
    scanf("%d%d",&N[0],&N[1]);
    for(int i=0;i<=1;i++)
        for(int j=1;j<=N[i];j++){
            int t;scanf("%d",&t);
            Sum[i][j]=Sum[i][j-1]+t;
        }
    scanf("%d",&X);
    memset(MinSum,0x7f,sizeof MinSum);
    for(int i=0;i<=1;i++)
        for(int len=1;len<=N[i];len++)
            for(int j=len;j<=N[i];j++)
                MinSum[i][len]=min(MinSum[i][len],Sum[i][j]-Sum[i][j-len]);
    for(int i=1;i<=N[0];i++)
        for(int j=1;j<=N[1];j++)
            if(MinSum[0][i]*MinSum[1][j]<=X)
                Ans=max(Ans,i*j);
    printf("%d",Ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值