题目大意:略 题目传送门

神题,不写长题解简直是浪费了这道题

贪心

考虑从0节点出发的情况,显然一直往前走不回头才是最优策略

如果起点是在中间某个节点$s$,容易想到,如果既要游览$s$左边的某些景点,又要游览$s$右边的某些景点,最优策略一定是先游览完一边,然后再穿过$s$节点去游览另一边

也就是说$s$节点一定只被穿过一次,且先被游览完的一边还要额外消耗$x$点代价,$x$是距离$s$最远的被游览的节点到s的距离

设计DP状态

现在只考虑从S出发往右走的情况

定义状态$dp[i][j]$表示第i天,现在在j点时最多游览的景点数

容易得到转移方程$dp[i][j]=max(dp[i-|j-k|][k]+|j-k|+a_{j})$

暴力枚举 $O(n^{3})$,线段树优化$DP\;\;O(n^{2}logn)$

两种状态的空间是$O(n^{2})$的,不论是空间还是时间都不可行

利用上述贪心结论,我们一定一直往前走不回头,除非我们回头到$s$然后走另一边

所以j这一维不用记录了,因为在路上走的距离总和是已知的

除了向右走的情况,还有向左走,向右走回到景点$s$,向左走回到景点$s$另外三种情况,设它们为$f[i][0],g[i][0],f[i][1],g[i][1]$,转移都很类似不过多赘述

缩减状态,$dp[i]$表示走了$i$天最多游览的景点数

枚举游览的最右侧端点$j$,$dp[i]$就是在$S$到$j$之间选择最大的$i-(j-S)$个景点

可以用可持久化权值线段树实现,每次查询在主席树上二分,时间$O(logn)$

决策单调性

上述过程依然没有解决时间$O(n^{2})$这一难题

假设我们现在要求解$f[i]$,即走了i天最多游览的景点数

暴力枚举所有位置,找到了决策位置$p$,

那么走$j \leq i-1$天时,$f[j]\leq f[i]$,且$f[j]$选择的点的集合一定是$S$的子集,$f[j]$的决策位置一定$\leq p$

走$j \geq i+1$天时,$f[j]\geq f[i]$,且$f[j]$选择的点的集合一定包含$S$,$f[j]$的决策位置一定$\geq p$

发现决策竟然是单调的

分治

用类似于[WF2017]Money for Nothing的方法分治

但这道题对有多个决策位置的处理方式略有不同

在分治过程中,设现在天数的分治区间是$[l1,r1]$,选择要求解$f[mid]$,可能的决策区间是$[l2,r2]$

暴力枚举$[l2,r2]$,找到了$f[mid]$的决策集合$T$,$T$中的每个元素都能作为$f[mid]$的决策,设其中最靠右的元素是b

$[l1,mid-1]$可能的决策区间是$[l2,b]$,因为并不知道哪个决策位置对应的点集合$S$的子集最优,所以$f[mid]$整个决策区间都可能作为$[l1,mid-1]$的决策

$[mid+1,r1]$可能的决策区间是$[b,r2]$,而不是集合里最靠左的元素到$r2$,下面给出简单证明

现在点权序列是2 8 9 1 3 11,走7步,最优方案有两种,决策位置是5,对应的点集$S$是8 9 3,决策位置是6,对应的点集$S$是9 11

容易发现,更靠右的决策位置,除了对应点集S里的点,剩余的权值较大的节点更多

$f[mid]$决策位置是5时,对应的点集S是8 9 3,还能被取的点是2 1

$f[mid]$决策位置是6时,对应的点集S是9 11,还能被取的点是8 2 1 3

因为大家都是贪心取,决策位置左边的最大的都取走了

最后把四种情况合并即可

另外这道题卡空间,需要对权值离散,不离散的话主席树树高会非常高,空间上容易被卡

别忘了离散只是为了优化线段树的结构,统计的时候把实际值还原回来

 #include <cstdio>
#include <cstring>
#include <algorithm>
#define N1 100010
#define M1 250010
#define ll long long
#define dd double
#define inf 23333333333333333ll
using namespace std; int gint()
{
int ret=,fh=;char c=getchar();
while(c<''||c>''){if(c=='-')fh=-;c=getchar();}
while(c>=''&&c<=''){ret=ret*+c-'';c=getchar();}
return ret*fh;
}
int b[N1];
struct Persist_SEG{
int sz[N1*],ls[N1*],rs[N1*],root[N1],tot; ll sum[N1*];
inline void pushup(int rt)
{
sz[rt]=sz[ls[rt]]+sz[rs[rt]];
sum[rt]=sum[ls[rt]]+sum[rs[rt]];
}
void update(int x,int l,int r,int rt1,int &rt2,int w)
{
if(!rt2||rt2==rt1){ rt2=++tot; sz[rt2]=sz[rt1]; ls[rt2]=ls[rt1]; rs[rt2]=rs[rt1]; sum[rt2]=sum[rt1]; }
if(l==r){ sum[rt2]+=w; sz[rt2]++; return; }
int mid=(l+r)>>; ll ans=;
if(x<=mid) update(x,l,mid,ls[rt1],ls[rt2],w);
else update(x,mid+,r,rs[rt1],rs[rt2],w);
pushup(rt2);
}
ll query(int K,int l,int r,int rt1,int rt2)
{
if(!rt2||!K) return ;
if(l==r) return min(sum[rt2]-sum[rt1],1ll*K*b[l]);
int mid=(l+r)>>; ll ans=;
if(sz[rs[rt2]]-sz[rs[rt1]]<K) ans=sum[rs[rt2]]-sum[rs[rt1]]+query(K-(sz[rs[rt2]]-sz[rs[rt1]]),l,mid,ls[rt1],ls[rt2]);
else ans=query(K,mid+,r,rs[rt1],rs[rt2]);
return ans;
}
}s; int n,S,D,ma,de;
int a[N1];
ll f[M1][],g[M1][];
void solve_f0(int l1,int r1,int l2,int r2)
{
if(l1>r1||l2>r2) return;
int mid=(l1+r1)>>,i,p=-; ll tmp,ans=-inf;
for(i=l2;i<=r2&&i-S<=mid;i++)
{
tmp=s.query(mid-(i-S),,ma,s.root[S-],s.root[i]);
if(tmp>ans) ans=tmp,p=i;
else if(tmp==ans) p=i;
}
f[mid][]=ans;
if(ans==-inf)
{
for(i=mid+;i<=r1;i++) if(i>=(l2-S)){ solve_f0(i,r1,l2,r2); break; }
return;
}
solve_f0(l1,mid-,l2,p); solve_f0(mid+,r1,p,r2);
}
void solve_f1(int l1,int r1,int l2,int r2)
{
if(l1>r1||l2>r2) return;
int mid=(l1+r1)>>,i,p=-; ll tmp,ans=-inf;
for(i=l2;i<=r2&&*(i-S)<=mid;i++)
{
tmp=s.query(mid-*(i-S),,ma,s.root[S-],s.root[i]);
if(tmp>ans) ans=tmp,p=i;
else if(tmp==ans) p=i;
}
f[mid][]=ans;
if(ans==-inf)
{
for(i=mid+;i<=r1;i++) if(i>=*(l2-S)){ solve_f1(i,r1,l2,r2); break; }
return;
}
solve_f1(l1,mid-,l2,p); solve_f1(mid+,r1,p,r2);
}
void solve_g0(int l1,int r1,int l2,int r2)
{
if(l1>r1||l2>r2) return;
int mid=(l1+r1)>>,i,p=-; ll tmp,ans=-inf;
for(i=r2;i>=l2&&S-i<=mid;i--)
{
tmp=s.query(mid-(S-i),,ma,s.root[i-],s.root[S-]);
if(tmp>ans) ans=tmp,p=i;
else if(tmp==ans) p=i;
}
g[mid][]=ans;
if(ans==-inf)
{
for(i=mid+;i<=r1;i++) if(i>=(S-r2)){ solve_g0(i,r1,l2,r2); break; }
return;
}
solve_g0(l1,mid-,p,r2); solve_g0(mid+,r1,l2,p);
}
void solve_g1(int l1,int r1,int l2,int r2)
{
if(l1>r1||l2>r2) return;
int mid=(l1+r1)>>,i,p=-; ll tmp,ans=-inf;
for(i=r2;i>=l2&&*(S-i)<=mid;i--)
{
tmp=s.query(mid-*(S-i),,ma,s.root[i-],s.root[S-]);
if(tmp>ans) ans=tmp,p=i;
else if(tmp==ans) p=i;
}
g[mid][]=ans;
if(ans==-inf)
{
for(i=mid+;i<=r1;i++) if(i>=*(S-r2)){ solve_g1(i,r1,l2,r2); break; }
return;
}
solve_g1(l1,mid-,p,r2); solve_g1(mid+,r1,l2,p);
} int main()
{
scanf("%d%d%d",&n,&S,&D); S++;
int i; ll ans=;
for(i=;i<=n;i++) a[i]=gint(),b[i]=a[i];
sort(b+,b+n+); ma=unique(b+,b+n+)-(b+);
for(i=;i<=n;i++)
{
a[i]=lower_bound(b+,b+ma+,a[i])-b;
s.update(a[i],,ma,s.root[i-],s.root[i],b[a[i]]);
}
solve_f0(,D,S,n); solve_f1(,D,S,n);
solve_g0(,D,,S-); solve_g1(,D,,S-);
for(i=;i<=D;i++){ans=max(ans,max( max(f[i][],g[i][]) , max(f[i][]+g[D-i][],f[i][]+g[D-i][]) )); }
printf("%lld\n",ans);
return ;
}

BZOJ 4367 [IOI2014]holiday (决策单调DP+主席树+分治)的更多相关文章

  1. [BZOJ4367][IOI2014]Holiday(决策单调性+分治+主席树)

    4367: [IOI2014]holiday假期 Time Limit: 20 Sec  Memory Limit: 64 MBSubmit: 421  Solved: 128[Submit][Sta ...

  2. 洛谷P4719 【模板】"动态 DP"&动态树分治

    [模板]"动态 DP"&动态树分治 第一道动态\(DP\)的题,只会用树剖来做,全局平衡二叉树什么的就以后再学吧 所谓动态\(DP\),就是在原本的\(DP\)求解的问题上 ...

  3. luogu P5892 [IOI2014]holiday 假期 决策单调性优化dp 主席树

    LINK:holiday 考虑第一个subtask. 容易想到n^2暴力枚举之后再暴力计算答案. 第二个subtask 暴力枚举终点可以利用主席树快速统计答案. 第三个subtask 暴力枚举两端利用 ...

  4. BZOJ 4826: [Hnoi2017]影魔 单调栈 主席树

    https://www.lydsy.com/JudgeOnline/problem.php?id=4826 年少不知空间贵,相顾mle空流泪. 和上一道主席树求的东西差不多,求两种对 1. max(a ...

  5. SRM12 T2夏令营(分治优化DP+主席树 (已更新NKlogN)/ 线段树优化DP)

     先写出朴素的DP方程f[i][j]=f[k][j-1]+h[k+1][i] {k<i}(h表示[k+1,j]有几个不同的数)  显然时间空间复杂度都无法承受   仔细想想可以发现对于一个点 i ...

  6. 【学术篇】CF833B TheBakery 分治dp+主席树

    题目の传送门~ 题目大意: 将\(n\)个蛋糕分成恰好\(k\)份, 求每份中包含的蛋糕的种类数之和的最大值. 这题有两种做法. 第一种是线段树优化dp, 我还没有考虑. 另一种就是分治+主席树. 然 ...

  7. BZOJ 3218 UOJ #77 A+B Problem (主席树、最小割)

    大名鼎鼎的A+B Problem, 主席树优化最小割-- 调题死活调不对,一怒之下改了一种写法交上去A了,但是改写法之后第4,5个点常数变大很多,于是喜提UOJ全站倒数第三 目前还不知道原来的写法为什 ...

  8. [BZOJ4826] [HNOI2017] 影魔 单调栈 主席树

    题面 因为是一个排列,所以不会有重复的.如果有重复就没法做了.一开始没有仔细看题目想了半天. 发现,如果是第一种情况,那么边界\(l\)和\(r\)就应该分别是整个区间的最大值和次大值. 然后,对于那 ...

  9. BZOJ 3514: Codechef MARCH14 GERALD07加强版 [LCT 主席树 kruskal]

    3514: Codechef MARCH14 GERALD07加强版 Time Limit: 60 Sec  Memory Limit: 256 MBSubmit: 1312  Solved: 501 ...

随机推荐

  1. 用JAVA的抽象类实现编码组合进度的灵活性

    都是实际开发逼出来的吧. 人类真灵活~~~~:) 就是将整个功能的实现在编程时,打散到一个一个文件中,提前写好核心算法, 在TEAM的实现方案确定下来之后,再进行组装. GuessGame.java ...

  2. mongodb--group聚合运算

    mongodb本质就是要做一个高性能,能简单则简单,不要把mongodb中的运算做的太复杂 count 最简单的一个聚合方法 distinct 选择结果中剔除重复的一个键值, 跟sql语句的效果是一样 ...

  3. C#--线程存储数据的机制

    面试题:线程存储数据的机制 Local variables 局部变量 临时存储 栈 Instance class fields 对象存储 堆 (堆的大小只有硬件的限制) Static local va ...

  4. Exchanger源代码剖析

    Exchanger是一个针对线程可以结对交换元素的同步器.每条线程把某个对象作为參数调用exchange方法,与伙伴线程进行匹配.然后再函数返回的时接收伙伴的对象.另外.Exchanger内部实现採用 ...

  5. Odoo(OpenERP)开发实践:数据模型学习

    作者:苏州-微尘 Odoo中,在Python类里定义的模型及字段信息,可在系统中直接查看.为用户开启技术特性权限后,就可以通过菜单 [设置->技术->数据结构->模型] 进入列表视图 ...

  6. 【CSS】CSS画矩形、圆、半圆、弧形、半圆、小三角、疑问框

    在网页中,常常会用到各种Icon,假设老是麻烦设计狮画出来不免有些不好意思,所以有时候我们也能够用CSS写出各种简单的形状.一来能够减轻他们的负担,二来也能够使用CSS替代图片.提高载入速度. 在网页 ...

  7. spring中使用HibernateTemplate或HibernateDaoSupport报类型转换错误

    使用spring的HibernateDaoSupport的时候.报错例如以下: java.lang.ClassCastException: java.lang.String cannot be cas ...

  8. springAOP注解方式实现日志操作

    通过自定义注解调用方法执行日志存储: package com.zktx.platform.log2; import java.lang.reflect.Method; import java.util ...

  9. BZOJ 4547 矩阵快速幂

    思路: 肯定每回只加最大值和次大值 如果 一开始的最大值>0且次大值<0 那就一直加 加到次大值>0 搞一个矩阵 推斐波那契数列 求和 就好- //By SiriusRen #inc ...

  10. jQuery Validate前端验证

    我们经常看到如下效果,那么它是如何实现的呢?看下面: 废话少说,直接上代码,大家直接Copy就能看到上面的效果啦. <html> <head> <title>验证内 ...