22牛客多校day9 B - Chiitoitsu 概论dp 线性逆元 + 差分优化

Two Frogs

题目描述

In the Lonely Mountain, there are a lot of treasure well protected by dwarfs. Later, one day, the last dragon Smaug came and sensed the treasure being. As known to all, dragons are always much too greedy for treasure. So definitely, the war between dwarfs and Smaug begins.

During the war, two goblins Alice and Bob are turned into frogs by Gandalf, The Grey. In front of them, there are nn lotus leaves in a line. In order to break the spell, they must jump from the 1 1 1 - st lotus leaf to the nn-th lotus leaf. If the frog is now on the ii-th lotus leaf instead of the n n n - th lotus leaf, it can jump to a lotus leaf in range ( i , i + a i ] (i, i + a_i] (i,i+ai] .

Goblins are lack of intelligence and it’s also true even after turned into frogs. So Alice and Bob will jump randomly, which means, they will separately pick an available lotus leaf in every jump uniformly at random.

Since Alice and Bob have already being playing games for decades, so they want to know the probability that they jump to the n n n - th lotus leaf with the same count of jumps.

输入描述

The first line contains an integer n ( 2 ≤ n ≤ 8   000 ) n (2 \leq n \leq 8\,000) n(2n8000), denoting the number of lotus leaf.

The second line contains n − 1 n−1 n1 integers a 1 , a 2 , … , a n − 1 a_1, a_2, \ldots, a_{n-1} a1,a2,,an1
, where the i i i - th integer a i ( 1 ≤ a i ≤ n − i ) a_i (1 \leq a_i \leq n-i) ai(1aini) indicates the range of lotus leaves that can be reached from the ii-th lotus leaf.

输出描述

Output a line containing a single integer, indicating the probability that Alive and Bob jump to nn-th lotus leaf with the same count of jumps, taken modulo 998   244   353 998\,244\,353 998244353.

Formally speaking, let the result, which is a rational number, be x y \frac{x}{y} yx as an irreducible fraction, you need to output x ⋅ y − 1   m o d   998   244   353 x \cdot y^{-1} \bmod{998\,244\,353} xy1mod998244353 , where y − 1 y^{-1} y1 is a number such that y ⋅ y − 1 ≡ 1 ( m o d 998   244   353 ) y \cdot y^{-1} \equiv 1 \pmod{998\,244\,353} yy11(mod998244353). You may safely assume that such y − 1 y^{-1} y1 always exists.

输入样例1:

5
1 1 1 1

输出样例1:

1

输入样例2:

5
4 3 2 1

输出样例2:

440198031

题意

n n n 个点以及 2 2 2 个青蛙, 2 2 2 个青蛙起始点均为 1 1 1号点,对于每个点 i i i ( 1 ≤ i ≤ n − 1 ) (1\leq i \leq n-1) (1in1) 都可等概率的跳跃至 ( i , i + a i ] (i, i + a_i] (i,i+ai] 号点,求两青蛙跳跃相同的步数跳至 n n n 号点的概论对 998   244   353 998\,244\,353 998244353 取模.

思路

考虑从前往后递推 d p [ i ] [ j ] dp[i][j] dp[i][j] 意为跳跃 i i i 步到达 j j j 点的概率 一共 n n n 个点最多跳跃 n − 1 n-1 n1

2 2 2 个青蛙花费相同步数到达 n n n 号点的概论 两个青蛙都要到达即概论的平方 即 ∑ i = 1 n − 1 d p [ i ] [ n ] 2 \sum\limits_{i=1}^{n-1}dp[i][n]^2 i=1n1dp[i][n]2

显然从前往后递推转移方程为 d p [ i + 1 ] [ j + ( 1 ∼ a [ j ] ) ] = dp[i+1][j+(1 \sim a[j])]= dp[i+1][j+(1a[j])]= d p [ i + 1 ] [ j + ( 1 ∼ a [ j ] ) ] + dp[i+1][j+(1 \sim a[j])] + dp[i+1][j+(1a[j])]+ d p [ i ] [ j ] / a [ j ] dp[i][j]/a[j] dp[i][j]/a[j]

暴力转移时间复杂度是 O ( n 3 ) O(n^3) O(n3) 的显然不行

观察转移方程可知每个点都是对其能跳跃到的区间做出相同的贡献 即区间加

然后对下一次跳跃的状态用前缀即可求值(注意每次转移都只与上次转移过来的值有关 故差分数组做完前缀后要清空)

这样的时间复杂度可以降到 O ( n 2 ) O(n^2) O(n2)

注意逆元先线性求好 不然带个 l o g log log T L E TLE TLE

我们可以先对 i n v [ 1 ] = 1 inv[1]=1 inv[1]=1 先赋好值 然后线性的从小到大去求出所需范围的逆元 证明如下:
假定我们需要求 i n v [ i ] inv[i] inv[i]
将模数定为 p p p
k = ⌊ p i ⌋ k=\lfloor \frac{p}{i} \rfloor k=ip, r = p r=p r=p m o d mod mod i i i
k ∗ i + r ≡ 0 k*i+r\equiv0 ki+r0 ( m o d (mod (mod p ) p) p)
移项可得 − k ∗ i ≡ r -k*i \equiv r kir ( m o d (mod (mod p ) p) p)
两边同除 i ∗ r i*r ir 可得 − k ∗ r − 1 ≡ i − 1 -k*r^{-1} \equiv i^{-1} kr1i1 ( m o d (mod (mod p ) p) p)
为了让逆元是正数我们加上一个模数 p p p 可得 p − k ∗ r − 1 ≡ i − 1 p-k*r^{-1} \equiv i^{-1} pkr1i1 ( m o d (mod (mod p ) p) p)
得证: i n v [ i ] = inv[i]= inv[i]= p − k ∗ r − 1 ≡ i − 1 p-k*r^{-1} \equiv i^{-1} pkr1i1 ( m o d (mod (mod p ) p) p)
i n v [ i ] = inv[i]= inv[i]= ( p − p / i ) ∗ i n v [ p (p-p/i) * inv[p (pp/i)inv[p m o d mod mod i ] i] i] m o d mod mod p p p

当然 高兴的话开个滚动数组维护也行 可以省点内存

Code

#include<bits/stdc++.h>
using namespace std;
#define __T int csT;scanf("%d",&csT);while(csT--)
#define endl '\n'
#define int long long
const int mod=998244353;
const double PI= acos(-1);
const double eps=1e-6;
const int N=2e5+7;

int n;
long long ans,sum;
long long dp[8003][8003],cf[8003];
long long a[8003],inv[8003];
void get_inv()
{
    inv[1]=1;
    for(int i=2;i<=n;i++)
    {
        inv[i]=(mod-mod/i)*inv[mod%i]%mod;
    }
}//线性求逆元
inline void sol()
{
    scanf("%lld",&n);
    get_inv();
    for(int i=1;i<n;++i)scanf("%lld",&a[i]);
    dp[0][1]=1;
    for(int i=1;i<n;++i)
    {
        for(int j=i;j<n;++j)
        {
            cf[j+1]=(cf[j+1]+dp[i-1][j]*inv[a[j]]%mod)%mod;
            cf[j+a[j]+1]=(cf[j+a[j]+1]-dp[i-1][j]*inv[a[j]]%mod+mod)%mod;
        }
        sum=0;
        for(int j=1;j<=n;++j)
        {
            sum=(sum+cf[j])%mod;
            cf[j]=0;
            dp[i][j]=sum;
        }
    }
    ans=0;
    for(int i=1;i<n;++i)ans=(ans+dp[i][n]*dp[i][n]%mod)%mod;
    printf("%lld\n",ans);
}
signed main()
{
    sol();
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柯西可乐

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值