vijos 1471 教主的游乐场
来源:互联网 发布:截面数据要做哪些检验 编辑:程序博客网 时间:2024/04/29 01:11
传送门
题解:首先这个题可以n遍bfs。
然后发现dp只记录一维状态不够,因为可能有环,不满足dp性质。
解决方案有如下两种:
1.于是找性质,发现非常有意思的一点是每个点可以向前跳到任何一个点。
考虑一条最优解路径,如果先向右跳了一步,然后又向左跳了一步,那么不如直接向右/左跳一步。
也就是最优解至多在第一次跳是向左的。
因此可以先处理一个点只往右跳的答案,这个显然可以用线段树维护出来。
然后再考虑每个点的答案等于只往右跳的答案和先向左跳一步再向右跳这两者取min。
后面一部分维护一个前缀最值即可做到O(n)。
考虑线段树常数巨大,第一部分注意到每次询问虽然强制在线但是区间的左端点递减,
因此可以用单调队列维护,每次在队列里面二分。
这个过程依旧可以优化,注意到队列里面实际上是若干区间,每次询问就是询问一个点所在区间的(最小)值,
而每次左面加一个点相当于把前面的几个区间合并。
本来是要用可并堆维护的,但是由于没有删除操作所以区间只需要维护一个最值即可。
这样用并查集维护可以做到O(nα(n))。
但是如果注意到之所以每次要在单调队列里面二分是因为询问右端点不单调,
或者在一开始dp的时候能够转化模型为:
给定若干区间,每次可以从当前区间跳到另一个左端点小于等于当前区间右端点的区间
不难发现最优解中的右端点必然是严格递增的。因此可以以右端点为阶段划分进行dp。
对区间按照右端点排序使用桶牌显然可以做到O(n)。
然后这样dp显然是可以用单调队列维护的,而且询问的右端点是单调的。
这样复杂度就可以做到O(n)。
但是我太懒了只写了个最笨的线段树。
代码:
#include<iostream>#include<cstring>#include<cstdio>#include<algorithm>#include<climits>#define MAXN 100010#define INF INT_MAX/2using namespace std;int f[MAXN],a[MAXN];struct segment{int l,r,v;segment *ch[2];}*rt;int build(segment* &rt,int l,int r){rt=new segment;rt->l=l,rt->r=r;rt->v=0;if(l==r) return 0;int mid=(l+r)>>1;build(rt->ch[0],l,mid);build(rt->ch[1],mid+1,r);return 0;}int query(segment* &rt,int s,int t){int l=rt->l,r=rt->r;if(s<=l&&r<=t) return rt->v;int mid=(l+r)>>1,ans=INF;if(s<=mid) ans=min(ans,query(rt->ch[0],s,t));if(mid<t) ans=min(ans,query(rt->ch[1],s,t));return ans;}inline int push_up(segment* &rt){return rt->v=min(rt->ch[0]->v,rt->ch[1]->v);}int update(segment* &rt,int p,int v){int l=rt->l,r=rt->r;if(l==r) return rt->v=v;int mid=(l+r)>>1;if(p<=mid) update(rt->ch[0],p,v);else update(rt->ch[1],p,v);return push_up(rt);}int main(){int n,m;scanf("%d%d",&n,&m);for(int i=1;i<=n;i++) scanf("%d",&a[i]);for(int i=1;i<=n;i++)if(a[i]+i>n) a[i]=n+1-i;build(rt,1,n+1);for(int i=n;i;i--){if(a[i]) f[i]=query(rt,i+1,i+a[i])+1;else f[i]=INF;update(rt,i,f[i]);}for(int i=1;i<=n;i++){if(i-1) f[i]=min(f[i],query(rt,1,i-1)+1);update(rt,i,f[i]);}for(int i=1;i<=m;i++){int x;scanf("%d",&x);if(i^m) printf("%d ",f[x]);else printf("%d\n",f[x]);}return 0;}
阅读全文
0 0
- Vijos 1471 教主的游乐场
- vijos 1471 教主的游乐场
- [vijos 1471]教主的游乐场
- vijos p1471 教主的游乐场(贪心)
- vijosP1471 教主的游乐场
- [vijos1471] 教主的游乐场
- JZOJ 1407. 教主的游乐场
- VIJOS P1617 超级教主
- Vijos 题目1470 教主的后花园(DP)
- 【二分】【高精度】Vijos P1472 教主的集合序列
- 最佳的代码游乐场
- 游乐场
- tensorflow 实现游乐场的网络
- 教主的花园
- 【枚举】教主的花园
- 你们的“教主”是谁?
- 3343: 教主的魔法
- [bzoj3343]教主的魔法
- 我是一个粉刷匠
- POJ 2886 Who Gets the Most Candies?
- 基于树莓派的AirPlay功能实现
- 你不知道的Vue.js(相关开源项目库集合)
- 添加上级目录模块
- vijos 1471 教主的游乐场
- 程序功能:建立一个带有头结点的单向链表,并将存储在数组中的字符依次转储到链表的各个结点中。
- 【NOIP模拟8.4】
- VS2015配置OpenCv2.4.13
- InnoDB关键特性之double write
- JAVA中创建JDBC以及对jdbc的封装(数据库为mysql)
- 编写程序STUDENT *Create(STUDENT studs[],int n)。 STUDENT是一个结构类型,包含姓名、成绩和指针域。
- CSU 1592:石子归并(区间DP)
- Scanner的next()和nextLine()的区别