[ACNOI2022]总差一步

《咒骂》

我也很想过,要写些《闲话》类的东西,然而考虑到其中所必定会带有的负能量,以及预想到校长和卷爷——或许还有狗狗——在评论区的辛辣的嘲讽,终究作罢。

说到底我并没有写《闲话》的资格。卷爷成绩好,所以他做什么都是合情合理的。我不是那样的。我什么权利也没有。我什么也不是。

我的心底只有寒冷、黑暗和死亡。我的生活只值得咒骂而不值得闲谈。我只能把它命名为《咒骂》而再没有别的内容。

可此后这念头就深深扎根在心底。像一座死火山,哪怕它是 “死” 的,它的内部也仍然是滚烫的岩浆。因此我借博客前的一点空间,稍微写点什么吧。

最近发生了很多不可思议的事,而且接二连三:先是角膜塑形镜意外遗失,接着牙套遗失,然后框架眼镜的眼镜腿断掉,现在考试连续垫底。

这似乎是某种预兆。某种我不愿去承认,但已经心知肚明的预兆。

但,或许,这也是好事。它至少让我的心理负担没那么重。

我已经对自己的弱小熟视无睹了。对自己的无能见怪不怪了。对自己的愚蠢毫不意外了。


题目

题目背景
因为 O U Y E \sf OUYE OUYE 过分内卷,像我一样的普通人已经 没有 生存 空间 了。

题目描述
考虑 2 k 2^k 2k 个叶子的线段树,每个节点的值是子节点的较大值。

叶子值是 [ 1 , 2 k ] [1,2^k] [1,2k] 给定排列。现在 q q q 次询问 x , y x,y x,y,问 x x x 值最多能在多少个节点上出现,如果交换至多 y y y 次叶子的值。

强制在线,且空间限制 16 M i B 16\rm MiB 16MiB

数据范围与提示
k ⩽ 19 k\leqslant 19 k19 q ⩽ 2 × 1 0 5 q\leqslant 2\times 10^5 q2×105

思路

y ≠ 0 y\ne 0 y=0,则只需判断某层上,是否有节点拥有至多 y y y 个大于 x x x 的值。

y = 0 y=0 y=0 时线段树没有变动,可以直接数出答案。故下略。

扫描线

看到大于 x x x,考虑从大到小依次处理 x x x

每次将一个新元素插入,这将影响到 k k k 层内各一个节点。然后我们要维护每层的 min ⁡ c n t \min cnt mincnt 。用数据结构维护,似乎是 O ( k 2 2 k ) \mathcal O(k^22^k) O(k22k) 的复杂度。

其实每层记录 min ⁡ \min min 的出现次数,发现 min ⁡ \min min 变化后暴力重新计算即可。因为有 L L L 个节点的层中, min ⁡ \min min 不会超过 2 k L 2^k\over L L2k,而每次增加需要 O ( L ) \mathcal O(L) O(L) 重新计算,因此 k k k 层的总复杂度 O ( k 2 k ) \mathcal O(k2^k) O(k2k)

这样我们就得到每个 x x x 想要在 t t t 个节点上出现时, y y y 至少是多少。如果存储下来,就可以在线处理所有询问了。复杂度 O ( k 2 k + q log ⁡ k ) \mathcal O(k2^k+q\log k) O(k2k+qlogk) 。若在线,空间复杂度 O ( k 2 k ) \mathcal O(k2^k) O(k2k),若离线,空间复杂度 O ( 2 k + q ) \mathcal O(2^k+q) O(2k+q)

二维偏序

扫描线 是每个 x x x 去查询可用的线段树节点。

反过来考虑:每个线段树节点能给 x x x 提供什么。

显然第 t t t 大的值 v v v 给出了限制:若 x ⩾ v x\geqslant v xv,则 y ⩾ t − 1 y\geqslant t{-}1 yt1 时能获得该区间的答案。

注意到每层只有 O ( 2 k ) \mathcal O(2^k) O(2k) 个这样的贡献,直接用数组暴力扫描线记下答案即可。

求出的这个数组和 扫描线 中得到的结果是相同的。

转换维度

注意到 半平面 中,给出的限制 y ⩾ t − 1 y\geqslant t{-}1 yt1 只有 L L L 种,其中 L L L 是单个节点的长度。

因此我们记录 f ( t ) f(t) f(t) 为,在 y ⩾ t − 1 y\geqslant t{-}1 yt1 时, x ⩾ f ( t ) x\geqslant f(t) xf(t) 可达到该层。

由于每层的 f f f 的长度和是 O ( 2 k ) \mathcal O(2^k) O(2k),空间复杂度就是 O ( 2 k ) \mathcal O(2^k) O(2k) 的。

询问仍然可以二分,时间复杂度 O ( k 2 k + q log ⁡ k ) \mathcal O(k2^k+q\log k) O(k2k+qlogk),空间复杂度 O ( 2 k ) \mathcal O(2^k) O(2k)

别出心裁

怎么强制在线?利用上一个答案。

然而答案只有 k k k 种,因此将这 O ( q k ) \mathcal O(qk) O(qk) 个询问离线下来,我们也得到可通过的做法。

代码

#include <cstdio>
#include <algorithm> // Almighty XJX yyds!!
#include <cstring> // oracle: ZXY yydBUS!!!
#include <cctype> // rainybunny root of the evil.
#include <functional>
using llong = long long;
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
# define rep0(i,a,b) for(int i=(a); i!=(b); ++i)
inline int readint(){
    int a = 0, c = getchar(), f = 1;
    for(; !isdigit(c); c=getchar()) if(c == '-') f = -f;
    for(; isdigit(c); c=getchar()) a = a*10+(c^48);
    return a*f;
}

const int MAXK = 19;
int a[1<<MAXK]; int* tag[MAXK];
int ori[1<<MAXK]; /** for y = 0 */

const int INF = 0x3fffFFFF;
int tmp[1<<MAXK];
int main(){
    int n = readint(), k = readint(), typ = readint();
    rep0(i,0,n) a[i] = readint()-1;
    rep0(j,0,k){ // merge for each layer
        tag[j] = new int[2<<j];
        std::fill_n(tag[j],2<<j,INF);
        int* l = a; int* r = a+(2<<j);
        for(; l!=a+n; r+=(2<<j)){
            int* const mid = l+(1<<j);
            ori[std::min(*l,*mid)] = j; // lose here
            std::merge(l,mid,mid,r,tmp,std::greater<int>());
            memcpy(l,tmp,(2<<j)<<2); // copy back
            for(int* i=tag[j]; l!=r; ++l,++i)
                *i = std::min(*i,*l); // corresponding
        }
    }
    ori[n-1] = k; // the final winner
    for(int q=readint(),x,y,ans=0; q; --q){
        x = readint(), y = readint();
        if(typ) x ^= ans, y ^= ans;
        if(!y){ printf("%d\n",ans=ori[x-1]); continue; }
        int l = 31-__builtin_clz(y); /** smaller than @a y */
        int r = 31-__builtin_clz(x--); /** too big to replace */
        if(l > r){ printf("%d\n",ans=r); continue; }
        while(l != r){
            const int mid = (l+r+1)>>1;
            if(tag[mid-1][y] <= x) // acceptable
                l = mid; else r = mid-1;
        }
        printf("%d\n",ans=r);
    }
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值