题解:P11392 [JOI Open 2019] 三级跳 / Triple Jump
unicorn1220 · · 题解
[JOI Open 2019] Triple Jump
题意
给定一个长为
-
L \leq i<j<k \leq R -
j-i \leq k-j
题解
暴力枚举情况太多,所以考虑去掉某些无需枚举的情况。
什么时候可以扔掉一个情况
这里的不劣于,需要考虑两个条件。第一,
也就是说,满足
然而,这种约束情况一不太好找,二过于苛刻,可能会导致去掉的情况不够多。
我们观察
我们惊奇的发现,这个缩小后的区间,正好对应了
现在来看还剩下多少有用情况。上面的式子意味着对于
这么少的情况可以直接用线段树维护。对于所有有用情况
最后,在处理询问时,有用区间应该按左端点降序插入线段树,同时边插边把当前左端点的询问做了,因为我们要保证插入线段树的区间也在询问范围内。
代码放个主函数吧。
signed main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
build(1,1,n);scanf("%lld",&Q);
for(int i=1,ai,bi;i<=Q;i++) scanf("%lld%lld",&ai,&bi),ask[ai].push_back(make_pair(bi,i));
for(int i=1;i<=n;i++)
{
while(!q.empty()&&a[i]>a[q.top()]) q.pop();
if(!q.empty()) qj[q.top()].push_back(i);
q.push(i);
}//找Li
while(!q.empty()) q.pop();
for(int i=n;i>=1;i--)
{
while(!q.empty()&&a[i]>a[q.top()]) q.pop();
if(!q.empty()) qj[i].push_back(q.top());
q.push(i);
}//找Ri
for(int i=n;i>=1;i--)//倒着插入
{
for(int j:qj[i]) update(1,1,n,2*j-i,n,a[j]+a[i]);
for(auto j:ask[i]) ans[j.second]=query(1,1,n,i,j.first);
}
for(int i=1;i<=Q;i++) printf("%lld\n",ans[i]);
return 0;
}