最有意思的专题了。做这个专题会上瘾。然而才做出来两个。(这个专题貌似是不可能做完的)
其实开这个坑的主要原因是要存ac代码,oj上存不了提答题的答案和生成代码。。。
小修和小栋猜♂数字:
$Description:$
这题非常无良。你也不知道你到底要还原出哪些,你也不知道几步完成能得满分。
题目大意就是让你写一份标程。。?
我第一次看这道题以为标程的询问次数是$O(nlogn)$级别的,然而事实上并不是。
标程能还原出$n-4$个位置,并且在最差情况下操作步数是$2n-4$。
可能这道题不算很难。但其实这道题不是很可想。
应该可以嗅到大力分类讨论的气息,但是依旧不知道从何下手。
事实上,你维护不出来的永远是最值和次值,关于最值你一无所知,关于次值你只知道大小但是分不清它与最大值的位置。
这四个东西虽说你求不出来,但是它对你求解其它数有很大帮助啊。
如果你能猜出最大值和最小值的位置,那么你查询这两个位置+任意一个位置,就能知道后者的值。
所以我们维护最值和次值可能出现的位置。具体不清楚,但是可以大致确定在最与次的范围内。
即对于序列的前4个值,通过4次询问找到哪两个较大哪两个较小,以此分为两组$\{ q10,q11\},\{ q20,q21\}$,同时我们可以确定次大值$v_1$与次小值$v_0$的大小。
接下来我们从两组中各取一个点$q10,q20$,与序列中下一个数$a_i$比较,设查询结果为$V$
若$v_0<V<v_1$,那么就知道我们维护的四个数是最,次值,而新加入的值位于中间作为中位数,也就是$a_i=V$
若$v_0=V$,那么我们就知道次小值变成了中位数,只有可能是$a_i<v_0$,新的次小值变成$a_i$,同时我们确定$a_{q10}=v_0$
若$v_0>V$,那么这代表的唯一情况是你查询到了新的次小值,它可能是$a_i$也可能是$q10$,于是我们可以回答$a_{q11}=v_0$。
$q10,a_i$取代它成为最/次小值。然而因为我们还需要维护次小值是多少所以还需要询问一次$v_1=ask(q10,a_i,q20)$
$v_1=V$与$v_1<V$的情况同理,不再赘述。
就这样对于每种情况我们都能还原出一个值,一共$n-4$个。
对于每种情况我们会调用$1$或$2$次$ask$,加上最初的$4$次于是在最差情况下是$2n-4$次。
所以就可以$AC$了。大型分类讨论稍恶心。
1 #include"guess.h" 2 void guess(int n){ 3 if(n<5)return; 4 int x[5],q10,q11,q20,q21,v0,v1; 5 x[1]=ask(2,3,4);x[2]=ask(1,3,4);x[3]=ask(1,2,4);x[4]=ask(1,2,3); 6 int s1,s2,s3;for(int i=2;i<5;++i)if(x[i]==x[1])s1=i;else s2=i; 7 for(int i=2;i<5;++i)if(i!=s1&&i!=s2)s3=i; 8 if(x[1]<x[s2])q20=1,q21=s1,q10=s2,q11=s3,v1=x[s2],v0=x[1]; 9 else q10=1,q11=s1,q20=s2,q21=s3,v1=x[1],v0=x[s2]; 10 for(int i=5;i<=n;++i){ 11 int V=ask(q10,q20,i); 12 if(v0<V&&V<v1)answer(i,V); 13 else if(V==v1)answer(q20,V),q20=i,v1=ask(q10,i,q21); 14 else if(V==v0)answer(q10,V),q10=i,v0=ask(q11,i,q20); 15 else if(V>v1)answer(q21,v1),q21=i,v1=V; 16 else answer(q11,v0),q11=i,v0=V; 17 } 18 }