题目链接:传送门
题意:有一串长度为n(2<=n<=1e6)的等差数列,每个数都为小于等于1e9的非负数,且打乱了,现在你有60次询问的机会,询问有两种形式:
1. > x 查询数组中是否含有大于x的数存在
2.? x 查询数组下标为x的数值
求出等差数列的差和最小值。
思路:比较清晰的,可以在30次里二分求出最大值,接下来应该就是随机取几个数来通过差gcd求d,最后可以求得最小值。
问题在于rand()随机的伪随机数比较差,效果不好。看了别人博客后发现了mt19937,没有深究,但是应该会比rand()好一些。
用法:
#include <iostream>
#include <random>
using namespace std;
int main()
{
mt19937 mt_rand(time(0));
cout << mt_rand() << endl;
return 0;
}
题目代码:
#include<bits/stdc++.h>
#include <random>
using namespace std;
int tim;
map<int,int>mp;
bool check(int x)
{
tim--;
cout<<"> "<<x<<endl;
int ans;
cin>>ans;
if(ans)return 1;
else return 0;
}
int gcd(int a,int b)
{
int r;
while(b>0)
{
r=a%b;
a=b;
b=r;
}
return a;
}
int ak[105];
int main()
{
mt19937 mt_rand(time(0));
mp.clear();
tim = 60;
int n;
cin>>n;
int l = 0,r =1000000000;
int ans;
while (l <= r)
{
int mid = (l + r)/2;
if(check(mid)) ans=mid,l = mid+1;
else r = mid-1;
}
ans++;
// cout << ans <<endl;
int cnt = 0;
while(tim--)
{
int p= mt_rand()%n +1;
int tt = 0 ;
while(mp[p]&&tt<1000000){
tt++;
p = mt_rand()%n +1;
}
mp[p] = 1;
cout<<"? "<<p<<endl;
cin>>ak[cnt];
cnt++;
if(cnt>=n)break;
}
sort(ak,ak+cnt);
for(int i = 0;i<cnt-1;i++){
ak[i]= ak[i+1] - ak[i];
}
sort(ak,ak+cnt-1);
int an = ak[0];
for(int i =1;i<cnt-1;i++){
an = gcd(ak[i],an);
}
cout<<"! "<<ans - (n-1) * an<<" "<<an<<endl;
}