[bzoj4367][IOI2014]holiday假期

本文针对IOI2014竞赛中的Holiday问题进行了详细的解析,介绍了如何通过动态规划来解决旅行者在有限时间内游览多个城市并最大化参观景点数量的问题。文章提供了完整的代码实现,展示了如何利用数据结构和递归思想来优化解决方案。

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

4367: [IOI2014]holiday假期

Time Limit: 20 Sec Memory Limit: 64 MB
Submit: 52 Solved: 7
[Submit][Status][Discuss]
Description

健佳正在制定下个假期去台湾的游玩计划。在这个假期,健佳将会在城市之间奔波,并且参观这些城市的景点。
在台湾共有n个城市,它们全部位于一条高速公路上。这些城市连续地编号为0到n-1。对于城市i ( 0 < i < n - 1 ) 而言,与其相邻的城市是i-1和i+1。但是对于城市 0,唯一与其相邻的是城市 1。而对于城市n-1,唯一与其相邻的是城市n-2。
每个城市都有若干景点。健佳有d天假期并且打算要参观尽量多的景点。健佳已经选择了假期开始要到访的第一个城市。在假期的每一天,健佳可以选择去一个相邻的城市,或者参观所在城市的所有景点,但是不能同时进行。即使健佳在同一个城市停留多次,他也不会去重复参观该城市的景点。请帮助健佳策划这个假期,以便能让他参观尽可能多的景点。

Input

第1行: n, start, d.
第2行: attraction[0], …, attraction[n-1].
n: 城市数。
start: 起点城市的编号。
d: 假期的天数。
attraction: 长度为n的数组;attraction[i] 表示城市i的景点数目,其中0≤i≤n-1。

Output

输出一个整数表示健佳最多可以参观的景点数。

Sample Input

5 2 7

10 2 20 30 1
Sample Output

60
HINT

假 设健佳有 7 天假期,有 5 个城市(参见下表),而且他由城市 2 开始。在第一天,健佳参观城市2的 20 个景点。第二天,健佳由城市 2 去往城市 3。而在第三天,健佳参观城市 3 的30 个景点。接下来的3天,健佳由城市 3 前往城市 0。而在第 7 天,健佳参观城市0的 10 个景点。这样健佳参观的景点总数是20+30+10=60,这是他由城市 2 开始、在 7 天假期内最多能参观的景点数目。
这里写图片描述

很容易发现可能的路线只有几种,一直向右,一直向左,向右走折回再向左走,或者向左走再向右走。
设f[i],g[i],f1[i],g1[i]分别表示向右走i步,向左走i步,向右走最终返回st总共走i步,向左走最终返回st总共走i步,得到的最大愉悦值。
然后答案就是max{max(f1[i]+g[m-i],g1[i]+f[m-i]),i=0……m}
以f[i]的求法为例
设f[i]取得最优值时到达的最远点为d[i]
可以发现很好的性质,d[i]是单调递增的,并且取值都在st到n之间
官方证明:
这里写图片描述
假设当前我们要算的是f[l]~f[r],且d[l]~d[r]的取值范围是x到y
mid=(l+r)/2 暴力枚举x到y的所有决策求出f[mid]和d[mid]
得到两个子问题(l,mid-1,x,d[mid])和(mid+1,r,d[mid],y)
分别递归求解
复杂度O(n log^2 n)

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define mid (L+R)/2
#define LL long long
const int N=100010;
const int O=300010;
const int M=2000010;
LL v[M],now,f[O],g[O],f1[O],g1[O],ans;
int root[N],l[M],r[M],sum[M],siz;
int n,start,m,size,w[N],a[N],fd[O],gd[O],f1d[O],g1d[O];
inline int in(){
    int x=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    return x;
}
inline void insert(int L,int R,int x,int &y,int z){
    y=++siz;
    if(L==R){
        sum[y]=sum[x]+1;
        v[y]=v[x]+(LL)a[L];
        return ;
    }
    l[y]=l[x];r[y]=r[x];
    if(z<=mid) insert(L,mid,l[x],l[y],z);
    else insert(mid+1,R,r[x],r[y],z);
    v[y]=v[l[y]]+v[r[y]];
    sum[y]=sum[l[y]]+sum[r[y]];
}
inline void query(int L,int R,int x,int y,int k){
    if(k<=0) return ;
    if(L==R){
        now+=(LL)min(k,sum[y]-sum[x])*(LL)a[L];
        return ;
    }
    if(sum[r[y]]-sum[r[x]]>=k) query(mid+1,R,r[x],r[y],k);
    else now+=v[r[y]]-v[r[x]],query(L,mid,l[x],l[y],k-(sum[r[y]]-sum[r[x]]));
}
inline void solve1(int L,int R,int x,int y){
    int i;
    if(L>R) return ;
    for(i=x;i<=y;++i){
        now=0;
        query(1,size,root[start-1],root[i],mid-(i-start));
        if(now>f[mid]||!fd[mid]) f[mid]=now,fd[mid]=i;
    }
    solve1(L,mid-1,x,fd[mid]);solve1(mid+1,R,fd[mid],y);
}
inline void solve2(int L,int R,int x,int y){
    int i;
    if(L>R) return ;
    for(i=y;i>=x;--i){
        now=0;
        query(1,size,root[i-1],root[start-1],mid-(start-i));
        if(now>g[mid]||!gd[mid]) g[mid]=now,gd[mid]=i;
    }
    solve2(L,mid-1,gd[mid],y);solve2(mid+1,R,x,gd[mid]);
}
inline void solve3(int L,int R,int x,int y){
    int i;
    if(L>R) return ;
    for(i=x;i<=y;++i){
        now=0;
        query(1,size,root[start-1],root[i],mid-(i-start)*2);
        if(now>f1[mid]||!f1d[mid]) f1[mid]=now,f1d[mid]=i;
    }
    solve3(L,mid-1,x,f1d[mid]);solve3(mid+1,R,f1d[mid],y);
}
inline void solve4(int L,int R,int x,int y){
    int i;
    if(L>R) return ;
    for(i=y;i>=x;--i){
        now=0;
        query(1,size,root[i-1],root[start-1],mid-(start-i)*2);
        if(now>g1[mid]||!g1d[mid]) g1[mid]=now,g1d[mid]=i;
    }
    solve4(L,mid-1,g1d[mid],y);solve4(mid+1,R,x,g1d[mid]);
}
int main(){
    int i,j;
    n=in();start=in();m=in();++start;
    for(i=1;i<=n;++i) w[i]=a[i]=in();
    sort(a+1,a+n+1);
    size=unique(a+1,a+n+1)-a-1;
    for(i=1;i<=n;++i){
        w[i]=lower_bound(a+1,a+size+1,w[i])-a;
        insert(1,size,root[i-1],root[i],w[i]);
    }
    solve1(1,m,start,min(n,start+m));
    solve2(1,m,max(1,start-m),start);
    solve3(1,m,start,min(n,start+m/2));
    solve4(1,m,max(1,start-m/2),start);
    for(i=0;i<=m;++i)
      ans=max(ans,max(f1[i]+g[m-i],g1[i]+f[m-i]));
    printf("%lld\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值