模拟测试20191027

$T1:666$

开始以为是数学

打了个表发现步数不超过$50$

直接模拟就好了诶

枚举步数,每次扫所有点尽量往后走就好了

 

$T2:123567$

考场$40pts$后就失去梦想了啊

正解是大神杜教筛(%%%大神$DeepinC$

先看$40pts$的柿子

$$\sum_{i=1}^{\sqrt n}\mu_{i} \times \frac{n}{i\times i}$$

看后边这个$\frac{n}{i\times i}$,是不是和某些东西有点像?

没错,除法分块!

可以发现$1e18$在这样的除法分块下只有约$188$万种取值

$\mu$ 可以直接杜教筛求前缀和

1 for(ll i=1,j;i*i<=n;i=j+1){
2     j=sqrt(n/(n/i/i));
3     ans+=(n/i/i)*(S(j)-S(i-1));
4 }
除法分块

T3:椎

考场$YY$一棵$Treap$然而暴毙了

正解线段树维护单调栈

我们考虑大根堆$Treap$的结构

1,如果把所有点按照$key$排序的话,他们的$lca$就是他们之间$val$最大的点

2,一个点$i$的深度就是 $从左维护单调栈到i时栈内数的个数+从右开始维护单调栈到i时栈内数的个数-1$

用线段树维护单调栈就可以了

怎么维护?

考虑$solve(l,r,v)$表示把$v$放入区间$[l,r]$的单调栈内后的单调栈

既然是线段树,那主要难点在于怎么合并两个区间

现在我们以左边为例分两种情况考虑

$1,[l,r]\subseteq 当前查询区间$

(1) 插入值$\geq$右区间最大值

  直接递归查询左边,即调用$solve(l,mid,v)$,因为右边一定都被弹出

(2) 插入值$<$右区间最大值

  先递归查询右边,即调用$solve(mid+1,r,v)$,然后把右边最大值插入并递归查询左边,即调用$solve(l,mid,max_{rc})$

  后面的操作维护的就是把右边插入左边后左边剩余个数

$2,[l,r]\cap 当前查询区间$

优先查询右边,即调用$solve(mid+1,r,v)$,同时用一个全局变量维护已经查询过的区间的最大值$lst$_$max$

然后如果$[l,mid]\cap 当前查询区间$ 把现在这个最大值扔到左区间查询,即调用$solve(l,mid,lst$_$max)$

最后就得到了$solve(l,r,v)$

但是这样复杂度不对,因为情况1有可能扫了整个被查询区间,复杂度退化成了$O(n^{2})$

然而我们发现1(2)情况中调用的$solve(l,mid,max_{rc})$和v无关

那么我们可以在插入一个数时把这玩意预处理出来

复杂度$O(nlog^{2}n)$

猜你喜欢

转载自www.cnblogs.com/mikufun-hzoi-cpp/p/11746698.html
今日推荐