UOJ Easy Round #7

本文介绍了解决特定区间问题的有效算法,包括短路问题中的路径优化、天路问题中的区间最大值计算,以及一种通用区间长度问题的解决方案。通过动态规划、贪心策略及数据结构的应用,实现了对不同规模区间问题的有效求解。
摘要由CSDN通过智能技术生成

短路
发现路线只会向右或下走,且一定是走到了某一个层环后沿环绕到右下然后用来时相同的花费走回去
枚举走到哪一个环,线性处理出走到这个环的最小花费
dp出左上走到第i层环左上角的最小花费,算贡献时贪心找1~i-1中最小的环

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1e9
using namespace std;

const int maxn = 110000;

int n;
int a[maxn]; ll sum[maxn];
ll f[maxn];

ll ans;

int main()
{
    ans=LLONG_MAX;
    scanf("%d",&n); ++n;
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);

    int mn;
    for(int i=n;i>=1;i--)
    {
        sum[i]=sum[i+1]+a[i];
        if(i==n) f[i]=a[i],mn=i;
        else
        {
            f[i]=f[mn]+(ll)(mn-i)*a[mn]+sum[i]-sum[mn];
            if(a[i]<a[mn]) mn=i;
        }
        if(mn==i) ans=min(ans,f[i]*2ll+(ll)(4*i-5)*a[i]);
    }
    printf("%lld\n",ans);

    return 0;
}

天路
对于每个区间长度k,求所有长度为k的区间中最大值减最小值的最大值
因为只要求与标准答案的花费不超过5%,不妨取 c=1.0501.0511.0521.05...
因为随着k递增,对应的答案一定递增,对每个ci,找最小的x满足k>=x的k的答案都>=ci,那么夹在ci和ci+1之间的k的答案都可以用ci代替
ci的值的个数是logV的,枚举每个ci,用n的时间维护两个队列或预处理之后rmq,O(n)找到x
O(nlogV)

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1e6
using namespace std;

const int maxn = 210000;
const double eps = 1e-7;

int n,u;
int na,ma,a[maxn];
int r[maxn];
int q1[maxn],h1,t1;
int q2[maxn],h2,t2;
double f[maxn];

int main()
{
    scanf("%d",&n); na=inf;
    for(int i=1;i<=n;i++) 
    {
        scanf("%d",&a[i]);
        na=min(na,a[i]); ma=max(ma,a[i]);
    }u=ma-na;

    double c=1.0;
    while(c<=u+eps)
    {
        h1=1,t1=0;
        h2=1,t2=0;
        int kk=0;
        for(int i=1;i<=n;i++)
        {
            r[i]=r[i-1];

            int cc=a[i];
            while(h1<=t1&&a[q1[t1]]>=cc-eps) t1--;
            while(h1+1<=t1&&cc-a[q1[h1+1]]>=c-eps) h1++;
            if(h1<=t1&&cc-a[q1[h1]]>=c-eps) r[i]=max(r[i],q1[h1]);
            q1[++t1]=i;

            while(h2<=t2&&a[q2[t2]]<=cc+eps) t2--;
            while(h2+1<=t2&&a[q2[h2+1]]-cc>=c-eps) h2++;
            if(h2<=t2&&a[q2[h2]]-cc>=c-eps) r[i]=max(r[i],q2[h2]);
            q2[++t2]=i;

            kk=max(kk,i-r[i]);
        }
        f[kk+1]=c;
        if(c<=100) c+=1.0;
        else c*=1.05;
    }
    for(int i=2;i<=n;i++) 
    {
        if(fabs(f[i])<eps) f[i]=f[i-1];
        printf("%d\n",(int)(f[i]+0.5));
    }

    return 0;
}

套路
对于所有区间长度k>=K,求(区间[l,r]内值两两差的绝对值最小值s(l,r)*(r-l))的最大值
当区间长度k较小时,枚举k,用f[k][i]代表s(i,i+k-1),那么有 f[k][i]=min(f[k1][i],f[k1][i+1],abs(a[i]a[i+k1]))
当区间长度k<=S时,处理出这些区间的答案复杂度是 O(nS)
当k>S时,由鸽巢原理可得 s(l,r)<=m/(S1) ,可以只考虑差值<=m/(S-1)的值两两之间的贡献
用r[c]表示c最后出现的位置,f[i][k]表示以i为右端点,s(l,r)=k的左端点最左位置,有 f[i][k]=max(f[i1][k],f[i][k1],r[c+k])
枚举区间右端点扫过去,对于当前的ai,枚举与其差值<=m/(S-1)的数统计这些数对答案的贡献,复杂度O(nm/S)
S=m 平衡复杂度,总复杂度 O(nm)

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;

inline int _min(const int a,const int b){return a<b?a:b;}
inline void up(int &a,const int &b){if(a<b)a=b;}
inline void down(int &a,const int &b){if(a>b)a=b;}
const int maxn = 210000;

int n,m,K,S,u;
int a[maxn];

int f[maxn];
int las[maxn],r[maxn];
int ans[maxn];

int main()
{
    scanf("%d%d%d",&n,&m,&K); S=sqrt(m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);

    for(int j=1;j<=n;j++) f[j]=m;
    for(int i=2;i<=S;i++)
    {
        for(int j=1;j+i-1<=n;j++)
        {
            down(f[j],_min(f[j+1],abs(a[j]-a[j+i-1])));
            up(ans[i],f[j]);
        }
    }
    u=m/max(1,S-1)+1;
    for(int i=1;i<=n;i++)
    {
        int cc=a[i];
        for(int j=0;j<=u;j++)
        {
            int &temp=r[j];
            if(cc-j>=1) up(temp,las[cc-j]);
            if(cc+j<=m) up(temp,las[cc+j]);
            up(r[j+1],temp);
            up(ans[i-temp],j+1);
        }
        las[cc]=i;
    }

    ll re=0;
    for(int i=K;i<=n;i++) re=max(re,(ll)(i-1)*ans[i]);
    printf("%lld\n",re);

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值