Do You Like Interactive Problems?
2023钉耙编程(10)D题:
Problem Description
现在有一个整数 x xx ( 1 ≤ x ≤ n ) (1 \le x \le n)(1≤x≤n),但你不知道 x xx 。
你可以进行以下的询问方式,选择一个随机的整数 y yy ( 1 ≤ y ≤ n ) (1 \le y \le n)(1≤y≤n) ,每次询问是相互独立的,询问后你会被告知 x xx 和 y yy 满足 x < y , x > y , x = y x < y,x > y,x = yx<y,x>y,x=y 三种关系中的哪一种。
现在给出具体 n nn 后,求期望询问次数。
Input
第一行输入一个整数 T TT ( 1 ≤ T ≤ 100 ) (1 \le T \le 100)(1≤T≤100),表示测试组数。
接下来 T TT 行每行输入一个整数 n nn ( 1 ≤ n ≤ 1 0 9 ) (1 \le n \le 10^9)(1≤n≤10
9
)。
拿到这道题首先看是问的期望询问次数。故使用概率的分布问题:
首先判断n=1时,明显可以看出来ans = 0;
其次判断n!= 1 时,分种情况
(1): 当这个x在两端时,也就是在1,n两者之间时:
1)我们猜到是x或者相邻的数的概率是2/n,还需要猜0次。
2)我们猜到其他点时概率为n-2/n,还需要再猜ans次;
于是我们得到了ans = 2/n * 0 + n-2/n * ans + 1 >> ans = n/2;
(2): 当这个x在中间时:
1)我们猜到x的概率时1/n,还需要猜0次;
2)我们猜到x左右相邻的数的概率是2/n,然后再去猜x需要2/n次;
3)我们猜到其他点时概率为n-3/n,还需要再猜ans次;
于是我们得到了ans = 1/n * 0 + 2/n * n/2 + n-3/n * ans + 1 >> ans = 2*n/3;
我们把两种情况合起来:
ans = 2/n * n/2 + n-2/n * 2*n/3 >> 2 * n-1/3;
ok!!? 推到这里估计都烦了,是不是以为直接输出2*n-1/3就行了???
不不不不! 他还要取模mod = 998244353
在这里还要用到拓展欧几里得算法进行取模运算,因为直接取模运算除以3会导致吞数,所以用到对模运算进行操作处理来防止这种情况发生。
以下代码:
#include<iostream>
using namespace std;
typedef long long ll;
const int mod = 998244353;
int t;
ll n,ans;
void exgcd(int a,int b,int &x,int &y)
{
if(!b)
{
x = 1,y = 0;
return;
}
exgcd(b,a%b,y,x);
y -= a/b*x;
return;
}
void solve()
{
int x,y;
cin>>n;
if(n==1)
{
cout<<0<<endl;
return;
}
ans = (2 * n - 1);
exgcd(3,mod,x,y);
ans*=x;
cout<<ans%mod<<endl;
return;
}
int main()
{
cin>>t;
while(t--)
{
solve();
}
return 0;
}