【UOJ 246】【UER #7】套路

本文探讨了一种算法问题:给定一系列数值,求解特定条件下的最大区间乘积值。通过动态规划与区间搜索的方法,实现了高效求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

给出n个数(<=n),设S(i,j)为i到j之间的两两数的绝对值最小值,求S(l,r)(rl)(r-l+1>=k)的最大值。

Solution

设K为选K个数,
先想一下枚举K怎么做,很显然要先求出S,
设f[i][j]为做到第i个,往前选j个的S值,有DP:fi,j=min(fi1,j1,fi,j1,|aiaij+1|)
再想一下如果K非常大,
那么S的值一定不会很大,所以我们就可以枚举S的值,看看往前最多可以选到哪,
现在我们要求以当前点i作为结尾的S=j的区间,最长有多长;
记录一下la[j]表示j这个数最后出现的位置,ca[j]表示所有S=j的区间中,区间的开头出现的最晚的位置,
枚举j,伴随着j的增大,区间的左端点l的位置一定越来越大,
要使当前的区间S=j,就要在上一个区间(S=j-1)算出的l的基础上,往后移,
那么就要使当前的区间不得使l<=min(la[ai+j1],la[aij+1]),也不得使l<=ca[j1]
所以取一个max即可。

我们发现当k=n时,复杂度最优为O(nn)

复杂度:O(nn)

Code

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#define foi(i,a,b) for(i=a;i<=b;i++)
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
const int N=200500,INF=2147483640;
int read(int &n)
{
    char ch=' ';int q=0,w=1;
    for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
    if(ch=='-')w=-1,ch=getchar();
    for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n,ans,m1,M;
int a[N],f[2][N];
int la[N*2],ca[N];
int main()
{
    int q,w;
    read(n),read(m1),read(m);M=666;m--;
    fo(i,1,n)read(a[i]),f[1][i]=N;
    fo(I,2,M)fo(i,I,n)
    w=(I+1)%2,ans=max(ans,(I>m)*(I-1)*(f[I%2][i]=min(f[w][i],min(f[w][i-1],abs(a[i]-a[i-I+1])))));
    la[a[1]]=1;
    fo(i,2,n)
    {
        q=1;
        fo(j,0,min(m1,M))
        {
            if(i-q+1>m)ans=max(ans,j*(i-q));
            if(a[i]>j&&la[a[i]-j])q=max(q,la[a[i]-j]),ca[j]=max(ca[j],la[a[i]-j]);
            ca[j]=max(ca[j],la[a[i]+j]);
            q=max(q,max(ca[j]+1,la[a[i]+j]));
        }
        la[a[i]]=i;
    }
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值