牛客练习赛70 F.曲调(离线,思维,权值线段树)
Posted issue是fw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了牛客练习赛70 F.曲调(离线,思维,权值线段树)相关的知识,希望对你有一定的参考价值。
不看题解完全想不到,写下思路缕一缕 (顺便福利以下大众)
对数组 a a a做前缀和,那么每次只需要找到 [ l , r ] [l,r] [l,r]内差值最小的两个数即可
但是这个也显然做不了,考虑对询问区间排序后按照 r r r端点离线
顺序扫描每个 i i i,考虑 a i a_i ai和前面数字 a q a_q aq形成的贡献
显然可以选择找 a i a_i ai最接近的 a q a_q aq,然而此时 q q q可能很小,很多区间更新不到
所以为了不遗漏任何答案,我们先找到最大的 k k k满足 k < i & & a k < = a i k<i\\&\\&a_k<=a_i k<i&&ak<=ai
显然,右端点大于等于 i i i且左端点在 k k k左边的区间都可以更新答案
然后我们再从 k k k往左边扫描,也就是找到一个最大的 k 1 < k & & a i > = a k 1 > a k k_1<k\\&\\&a_i>=a_{k_1}>a_k k1<k&&ai>=ak1>ak
因为这样虽然能更新的区间不如上一个多,但是更新的值却变为更优的 a b s ( a k 1 − a i ) abs(a_{k_1}-a_i) abs(ak1−ai)
然后一直重复这个步骤去更新区间即可
看上去完全没有优化,因为每次扫描的复杂度为 O ( n ) O(n) O(n)
然而我们发现,第一次我们找到的是索引最大的 k k k满足 a k < a i a_k<a_i ak<ai
第二次找到的是最大索引的 k 1 k_1 k1满足 a k < a k 1 < a i a_k<a_{k_1}<a_i ak<ak1<ai
这个 k 1 k_1 k1的贡献是 a b s ( a i − a k 1 ) abs(a_i-a_{k_1}) abs(ai−ak1),而 a k , a k 1 a_k,a_{k_1} ak,ak1的贡献是 a b s ( a k 1 − a k ) abs(a_{k_1}-a_k) abs(ak1−ak)
显然后者更优,而且能覆盖的区间范围也更广,所以我们的 k 1 k_1 k1不是乱选的,至少需要满足
a i + a k 2 + 1 < = a k 1 < = a i \\frac{a_i+a_k}{2}+1<=a_{k_1}<=a_i 2ai+ak+1<=ak1<=ai
于是发现 a k 1 a_{k_1} ak1的值域减小的非常快,似乎是 l o g log log次的
所以现在的任务就是从 a [ 1... i − 1 ] a[1...i-1] a[1...i−1]找 a q ∈ [ a i + a k 2 , a i ] a_q\\in[\\frac{a_i+a_k}{2},a_i] aq∈[2ai+ak,ai]的最大索引 q q q
这个上个动态开点权值线段树即可,区间维护最大索引,动态插入 a i a_i ai即可
接下来就是更新左端点小于等于 k k k的所有区间,这只需要一个区间 m i n min min,同样上线段树
然后就在 O ( n l o g ( n ) l o g ( w ) ) O(nlog(n)log(w)) O(nlog(n)log(w))的复杂度做完了, w w w是前缀和值域
细节
Ⅰ.由于是做前缀和,需要把区间的左端点减一
Ⅱ.权值线段树下标不能是负数,为了不离散化,直接对每个前缀和加上 1 0 15 10^{15} 1015
Ⅲ.缩小值域的时候,注意取整问题
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid (l+r>>1)
#define lson ls,l,mid
#define rson rs,mid+1,r
const int inf = 2e15;
const int maxn = 3e5+10;
const int N = 2e6+10;
int n,m,ans[maxn],b[maxn];
struct p
{
int l,r,id;
bool operator < ( const p&tmp ) const { return r<tmp.r; }
}a[maxn];
int mx[N],LS[N],RS[N],id,root;
void insert(int &rt,int l,int r,int pos,int val)//单点更新
{
if( !rt ) rt = ++id;
mx[rt] = max( mx[rt],val );//因为val是单调递增的
if( l==r ) return;
if( pos<=mid ) insert( LS[rt],l,mid,pos,val );
else insert( RS[rt],mid+1,r,pos,val );
}
int ask(int rt,int l,int r,int L,int R)//查询[L,R]内序号最大的
{
if( !rt || l>R || r<L ) return -1;
if( l>=L && r<=R ) return mx[rt];
return max( ask(LS[rt],l,mid,L,R),ask(RS以上是关于牛客练习赛70 F.曲调(离线,思维,权值线段树)的主要内容,如果未能解决你的问题,请参考以下文章
GYM 101350 F. Monkeying Around(线段树 or 思维)
Codeforces1539 F. Strange Array(思维,线段树)
Educational Codeforces Round 47 (Rated for Div. 2)F. Dominant Indices 线段树合并