• CF1114E Arithmetic Progression(交互题,二分,随机算法)


    既然是在CF上AC的第一道交互题,而且正是这场比赛让我升紫了,所以十分值得纪念。

    题目链接:CF原网

    题目大意:交互题。

    有一个长度为 $n$ 的序列 $a$,保证它从小到大排序后是个等差数列。你不知道这个序列长什么样,但你可以询问:

    • $? i$ 表示询问 $a_i$ 的值;
    • $> x$ 表示询问序列中是否存在严格大于 $x$ 的数。

    你可以问不超过 $60$ 个询问。

    现在,你需要求出这个等差数列的首项(也就是 $a$ 中的最小值)和这个等差数列的公差(也就是等差数列中相邻两个数的差)。

    $2le nle 10^6,0le a_ile 10^9$。


     看,第二个操作,明显暗示我们要二分最大值。(除了这个也没啥用了吧?)

    用掉 $log 10^9=30$ 个操作。

    令 $mx$ 为我们二分出的最大值。

    设公差为 $d$,那么 $forall 1le ile n,d|(mx-a_i)$。

    也就是如果我们知道的 $a_i$ 有 $x_1,x_2,x_3dots,x_k$,那么 $d|gcd(mx-x_1,mx-x_2,mx-x_3dots,mx-x_k)$。

    但这样就有一个严峻的问题:如果 $k$ 不够大,直接 $gcd$ 得到的可能不是答案!

    无路可走了,只有一个想法:随机!

    我们随机 $30$ 个 $i$,询问这些 $a_i$ 的值。(为什么要随机呢?因为如果直接取前 $30$ 个 $a_i$ 很可能会被毒瘤卡掉)

    $d$ 就是所有 $mx-a_i$ 的 $gcd$ 了。

    ???这样不会错吗?

    分析一下,我们设 $a_1=mx-k_1d,a_2=mx-k_2ddots a_n=mx-k_nd$。那么我们求出的 $gcd$ 就是随机出的 $30$ 个 $k_i$ 的 $gcd$。

    在 $10^6$ 中随机选 $30$ 个数,$gcd$ 不为 $1$ 的概率就是出错的概率。

    大致估算一下:

    选出的 $k$ 都是 $2$ 的倍数的概率是 $frac{1}{2^{30}}$。

    选出的 $k$ 都是 $3$ 的倍数的概率是 $frac{1}{3^{30}}$。

    等等等等……

    这样估算的话,出错概率是不会超过 $10^{-8}$ 的。

    其实有更准确的求法,就是莫比乌斯反演,但由于我们只是来证明正确性的,就不讲了。

    直接引用官方题解:

    By some maths, we can find out the probability of our solution to fail being relatively small — approximately $1.86185 imes 10^{-9}$.

    直接上rand()就做完了……

    了吗?

    对了,这就是出题人恶毒所在了……

    rand()和random_shuffle()的上界都是32767,访问不到后面大部分的元素。

    于是出题人就把恶毒的数放在了前面……

    那就用自己的rand()啊!写过treap吗?把那个rand搬过来就能用了!

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> PII;
    const int maxn=1000100;
    #define MP make_pair
    #define PB push_back
    #define lson o<<1,l,mid
    #define rson o<<1|1,mid+1,r
    #define FOR(i,a,b) for(int i=(a);i<=(b);i++)
    #define ROF(i,a,b) for(int i=(a);i>=(b);i--)
    #define MEM(x,v) memset(x,v,sizeof(x))
    inline int read(){
        char ch=getchar();int x=0,f=0;
        while(ch<'0' || ch>'9') f|=ch=='-',ch=getchar();
        while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
        return f?-x:x;
    }
    int n,cnt,pt[maxn];
    inline int rnd(){    //自己的rand
        static int seed=2333;
        return seed=(((seed*666666ll+20050818)%998244353)^1000000007)%1004535809;
    }
    int gcd(int x,int y){
        return y?gcd(y,x%y):x;
    }
    int main(){
        n=read();
        if(n<=60){    //n<=60,随便玩
            int mx=0,mn=INT_MAX;
            FOR(i,1,n){
                printf("? %d
    ",i);
                fflush(stdout);
                int x=read();
                mx=max(mx,x);
                mn=min(mn,x);
            }
            printf("! %d %d
    ",mn,(mx-mn)/(n-1));
            fflush(stdout);
            return 0;
        }
        int l=0,r=1e9;
        while(l<r){    //二分最大值
            int mid=(l+r)>>1;
            printf("> %d
    ",mid);
            cnt++;
            fflush(stdout);
            int ver=read();
            if(ver) l=mid+1;
            else r=mid;
        }
        int mx=r,ans=0;
        FOR(i,1,n) pt[i]=i;
        FOR(i,1,n) swap(pt[i],pt[rnd()%n+1]);
        FOR(i,1,min(n,60-cnt)){    //随机60-cnt个元素
            printf("? %d
    ",pt[i]);
            fflush(stdout);
            int x=read();
            ans=gcd(ans,mx-x);    //取gcd
        }
        printf("! %d %d
    ",mx-(n-1)*ans,ans);    //首项=末项-(项数-1)*公差
        fflush(stdout);
    }
    View Code
  • 相关阅读:
    Spark 集群 任务提交模式
    Spark RDD 宽窄依赖
    Spark 资源调度包 stage 类解析
    堆内内存与堆外内存
    Spark笔记(一)
    scala 语言特性
    Centos7.4 Storm2.0.0 + Zookeeper3.5.5 高可用集群搭建
    fs模块他的作用是(文件夹)
    事件循环机制
    简单的下路由(我们可以在控制器中看到路由参数)
  • 原文地址:https://www.cnblogs.com/1000Suns/p/10362096.html
Copyright © 2020-2023  润新知