Wannafly挑战赛25 C 期望操作数

链接:https://www.nowcoder.com/acm/contest/197/C
来源:牛客网
 

题目描述

Nqijij 有一个数x,和一个神秘权值 q, 满足 x <= q, 每一次nqijij会随机x 变成 [x, q] 中的一个随机数,nqijij想要知道期望多少次操作之后x 变为q。
由于nqijij 是一个精力充沛的人,所以他总共会选择 T 次x 和q 进行操作,对于每一次操作,你需要输出期望多少次操作之后x 变为q 在 998244353 模意义下的值。

(令 ans = x / y, x, y 为正整数且 题目保证 x、y 与998244353 互质,则输出x * (y^(-1)), (y^(-1)) 表示y在998244353下的乘法逆元,可以证明这样的逆元存在)

题目保证 T <= 1e6, q <= 1e7, x <= 1e7;

输入描述:

第一行一个数T
接下来T行
每行两个整数 x, q (x <= q)

输出描述:

输出T行
第 i 行的正整数 表示第i次询问的答案。

示例1

输入

复制

2
7 8
15 18

输出

复制

2
831870297

说明

对于第一个样例
每一次有0.5的概率结束利用简单的求和可知是2.

 

递推求逆元模板

//求逆元,当n特别大时,效果特别好
//begin
LL inv[N];
inline void get_inv()
{
    inv[1] = 1;
    for(int i=2; i<N; i++) {
        if(i >= mod) break;
        inv[i] = (mod - mod / i) * inv[mod % i] % mod;
    }
}
//end

 

对于这种无限次的选择,自己就会很懵逼,其实直接求确实是不存在,但是我们可以

1.找到状态表示,找到递推式

2.给定初始值

然后我们推一下就好了,基本上都是很简单的,不要恐惧

这道题,我们可以考虑,有用的就是两个人之间的差值。

所以我们假设 结尾在q,

那么我可以在

1, 用 f[1] 表示从1点到 n点的期望步数,

可以得到  f[1]=1/q*f[1]+1/q*f[2]+...+1/q*f[q-1]+1

在2的时候,可以得到

f[2]=1/(q-1)*f[2]+1/(q-1)*f[3]+...+1/(q-1)*f[q-1]+1

在其他点同理。 然后倒着退回来就好了。

可能那个1比较难理解,我们可以换个角度,如果我们想去q的话,是可以1步直接到的. 要考虑特殊点,特殊值,要不然,没有出口了,递归不能终止了。

 

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;

#define rep(i,a,b) for(int i=a;i<b;++i)
#define per(i,a,b) for(int i=b-1;i>=a;--i)

#define clk  clock()

const int mod=998244353;
const int N=1e7+10;


LL f[N];

LL inv_mod(LL base,int n)
{
    LL ans=1;
    while(n) {
        if(n&1)ans=ans*base%mod;
        base=base*base%mod;
        n>>=1;
    }
    return ans%mod;
}

//求逆元,当n特别大时,效果特别好
//begin
LL inv[N];
inline void get_inv()
{
    inv[1] = 1;
    for(int i=2; i<N; i++) {
        if(i >= mod) break;
        inv[i] = (mod - mod / i) * inv[mod % i] % mod;
    }
}
//end


int main()
{
    //double t1=clk;
    //for(int i=1;i<N;i++)inv[i]=inv_mod(i,mod-2);//求逆元超时,nlog(n) 也是太高
	get_inv();
	
    f[1]=2;
    LL sum=f[1];
    for(int i=2; i<N; i++) {
        f[i]=(sum+i+1)%mod*inv[i]%mod;
        sum=sum+f[i];
        if(sum>=mod)sum-=mod;
    }
    //double t2=clk;
    //printf("%.2f\n",(t2-t1)/CLOCKS_PER_SEC);
    int T;
    scanf("%d",&T);
    while(T--) {
        int x,q;
        scanf("%d %d",&x,&q);
        if(q==x) {
            puts("0");
        } else {
            printf("%d\n",f[q-x]);
        }
    }
    return 0;
}

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值