【题解】【模拟】 【一眼dp....然后、就没有然后了。。。】 【实际上,就是个模拟,只是不好想到】 【首先,考虑特殊情况:当l=0,但s≠1时,肯定是无解的(每个格都要遍历到,如果但却不能往左走,那么根本无法做到);当l=n-1,但s≠n时,也是无解的(每个格都只能遍历一遍)】 【接着,考虑s=1,t=n的情况。在这种情况下,若l≠0,那么必须要走“回头路”,这样一来,就有至少l个区间被重复走三次(从左往右走的时候经过了一遍,因为要走“回头路”,所以在从右往左走的时候也会再经过一遍,但因为最终是向右走,那么还要再经过一遍)。】 【推广上面这种方法,即s、t两侧还有区间,这些区间都是要走两遍的,[s,t]区间里就用上面的方法找出最小的l个区间。那么可知,l越小,总值越小,因为,在s、t的两侧不论走多少“回头路”,值都不变,所以可以尽量把l分布在两侧。所以,要首先把l尽量放到s外侧,如果l<s,那么就在“回头路”全部在s外侧走;其次,如果l-s=n-s-1,那么,把剩下的走“回头路”的次数全部放到t外侧;如果这两个条件都不满足,在枚举t前,先将l-=s,每枚举到一个t,若l<n-t,那么,把剩下的走“回头路”的次数全部放到t外侧,不然先将l-=(n-t),再在[s,t]中寻找较小的l个区间】
【[s,t]区间内的区间值用小根堆维护,每次取出较小的l个区间,重复三次,这个地方可以继承前面的,由于在t不断往后移的前提下,[s,t]区间内的l只会越来越大,所以能保证单调,可以不断往后更新】 【要注意每个点只能遍历一次,所以,在处理[s,t]区间内的情况时,一个区间能够加入,必须保证区间的右边界距s大于2个点,不然,走“回头路”会导致重复经过点】 【上述过程要做两遍,因为我们在找t时,是默认s<t的,但是,t也有在s左边的情况。所以把序列翻转、取反,再做一遍。将s<t的最小值和s>t的最小值比较、输出】
#include #include #include #include #define ll unsigned long longusing namespace std;struct node{ ll val; bool operator < (const node &x)const { return val>x.val; }};int n,l,s,x[200010],y[200010];inline ll solve(int l,int s,int t,int a[]){ priority_queue que; if(l s) ss.val=a[i-1]-a[i-2],que.push(ss); while(!que.empty()&&tt s2?s2:s1)); return 0;}