CF1480C Searching Local Minimum

传送门


题意

交互
给定\(n\), 有一个\(1-n\)的排列, 每次你可以询问\(x\)位置的数, 询问次数不超过\(100\)
求任意一个位置k, 使得\(a_k < \min(a_{k-1}, a_{k+1})\), 我们认为\(a_0=a_{n+1}= +\infty\)

题解

非常炫酷的交互!

当你看到题目的时候, 1e5的排列, 只能询问一个数, 而非某个区间的权值~!!!
只能100次, 结果几乎没有单调性, 完全独立,那不是酷毙了!!!让人有一种十分想切得感觉

炫酷题


显然大概是要二分啥的 , 问题在于某个位置的数, 并没有单调性
没有单调性, 那我们就给他创造单调性!,.....emm创造不出来
那么是不是由于没有单调性的情况非常简单?单独解决掉没有单调性的的情况后就有单调性了?....
唉对, 如果不是特殊构造的数据, 那么一个数比相邻得数小应该很常见吧

经过这样的思考,您会开始考虑:
如果\(a_2 > a_1\) 直接输出1,\(a_{n-1} > a_n\) 直接输出n,
除此之外, a数组一开始必然下降, 最后必然上升, 那么整个数组必然如此:
image

显然, 每个山谷都能成为答案,也只有山谷能成为答案

这时候, 你应该想到三分, 只有一个山谷显然可以三分, 复杂度也接近
一种想当然的想法是, 直接三分必然会趋近其中一个山谷, 得到答案
事实证明: 大多数情况也是如此, 并且会得到一个wrong on test61的罚时


关于三分法本质的探究和一种解决方案

为什么会wa呢, 我们来考虑一种情况
image
难道是因为我们没有判断左右两边导致的?但事实上这种情况也同样会导致
image
所以我们的做法假了为什么会假呢?这时候我们要考虑三分法本质
对于单谷函数, 每次取两个点假如是这样
image
这两个点呈上升趋势, 山谷要么在两点之间, 要么在两点左边, 我们把右边界缩小到第二个点, 实质上是在定位山谷

在有多个山谷时, 这种单调性依然成立!
image
wa的原因实际上是因为在有多个山谷的时候, 几次三分定位的山谷不同

image
更准确的来说, 每次三分(见上图)实际上是定位了若干个山谷(左边黑色部分), 抛弃了一些山谷(红色部分)
仔细思考, 我们只要避免之后的定位定位到抛弃的山谷即可


运用跳跃的思维, 您会得到一种解决方案

我们记录每次询问交互库得到的最小值, 以及所在的位置
假如在某次三分中,取到两个点都大于最小值,分类讨论:

如果两个点都在最小值左边, 将左端点设成三分取的第二个点
都在右边,就把右端点设成第一个点
在两点之间, 显然中间存在山谷, 让左端点等于第一个点, 右端点等于第二个点

这样做是没有问题的, 可以保证能够定位到某个山谷, 但是为什么不会定位到被抛弃的山谷呢?
假如某一时刻是这样的
image
在这张图里面, 每个断点会被断开, 是因为它们右边或边必然存在一个点比他小,
如果要定位到一个被抛弃的山谷, 显然只有两个点都在下图灰色部分中
image
这时候两个点都会比断点大, 而记录的最小值一定小于断点, 所以, 这时候会定位到他们左边, 而非抛弃山谷

时间复杂度当然也是对的, 因为每次出现这个情况, 长度都会缩小到原来的三分之一

很妙啊


Impl

int l=1, r=n;
    int minp=-1, minv=0x3f3f3f3f;
	// 感谢@wzx大佬的hack, 这里不能将初始值设为n, 要设为正无穷
    while(l < r){
        int mid = (r-l+1)/3;
        int lmid=min(r, l+mid), rmid=max(l, r-mid);
        if(lmid > rmid) swap(lmid, rmid);
        if(lmid == rmid){
            if(lmid > l) lmid--;
            else rmid++;
        }

        int lans=get(lmid), rans=get(rmid);

        if(min(lans, rans) > minv){
            if(rmid < minp){
                l = rmid+1;
            }else if(lmid > minp){
                r = lmid-1;
            }else{
                l=lmid+1, r=rmid-1;
            }
        }else {
            if(lans < rans) minv=lans, minp=lmid;
            else minv=rans, minp=rmid;
        }

        if(lans < rans) r = rmid-1;
        else l = lmid+1;
    }
    pri(l);
posted @ 2021-11-17 08:36  ltdJcoder  阅读(43)  评论(0编辑  收藏  举报