·英文题,述大意:
有一个长度为n(n<=50000)的单链表,里面的元素是递增的。链表存储在一个数组里面,给出长度n、表头在数组的下标和一个值w。题目要求求出链表中大于等于w值的元素中的最小元素。注意,这道题是一道interactive。由于链表是未知的,最多可以进行1999个询问,询问形式:?i。表示询问数组下标为i,询问后,会得到一个答案组(val,Next),表示询问的元素是val,链表下一位所在的数组下标是Next。最终如果找到答案输出:!ans,如果没有找到答案输出:! –1。
分析:
题目描述有点不准确,文中"没有找到答案"的意思是序链表中不存在满足条件的元素,而不仅仅是询问没找到。
观察数据范围:50000对1999询问,这看上去是不可能的,因为交互题是询问后就在线回答,大概意思就是你问测评机问题,它回答你……所以这看上去就是在蒙答案,蒙不对就完了(50000这么大,肯定完了)!
接下来用到随机算法。我们将总数1999拆成a+b。a表示我们先随机生成a个询问,表示在a个询问中最靠近答案的那个向后暴力行走链表b次。上文一句话就概括了本题的做法——为什么这样做是对的?我们将链表从左向右画出来(注意,不是把数组画出来),那么从左向右元素是递增的。我们现在尝试计算可能会遗漏答案情况的概率:
如图,蓝色的点就是随机的点。然后我们会找比val小但是又最近的点,那么向前走链表b次,看看是否找到答案。如果我们不能找到答案,那么由图可知,只有当L>b时,我们的算法是错误的。现在来算一下概率:
对于一个随机点,它不会落在答案点前面b长度的区间内的概率是:
P1=(n-b)/n
那么,由于我们随机了a个点,可得没有点落在答案点前b长度的区间的概率是(这个当然表示我们算法错误的概率啊):
Pall=((n-b)/n)a
这个呢,因为啊a+b=1999,我们令x=b,y=1-((n-x)/n)1999-x
那么可以看出y表示我们的算法能够正确的概率,我们将这个函数分析一下(比如做出图像):
我们发现当x=1000左右时,y几乎是1了(更精确见图中数据)。所以这道题随机算法竟然可以这样达到美妙!所以我们最终的方法就是:先随机询问 1000个点,然后从最优点走999次,判断答案是否存在了啦啦。
最后一个代码提醒:这道题直接用rand()会在第六个测试点WA,原因是随机数不均匀,我们需要改成:rand()*rand()*rand()……
CF型短代码:
1 #include<bits/stdc++.h> 2 #define _ fflush(stdout) 3 #define go(i,a,b) for(int i=a;i<=b;i++) 4 int n,val,a=-1,T,w,Next,l; 5 int main() 6 { 7 srand(time(NULL)); 8 scanf("%d%d%d",&n,&l,&val); 9 T=1000;while(T--) 10 { 11 printf("? %d ",1ll*rand()*rand()*rand()%n+1);_; 12 scanf("%d%d",&w,&Next); 13 if(w<val&&w>a)a=w,l=Next; 14 } 15 T=999;while(T--) 16 { 17 if(l==-1)break; 18 printf("? %d ",l);_; 19 scanf("%d%d",&w,&Next);l=Next; 20 if(w>=val){printf("! %d ",w);_;return 0;} 21 } 22 puts("! -1");_;return 0; 23 }//Paul_Guderian
又一种信仰将要破灭,又一个时代将要来临.———汪峰《风暴来临》