随机坏道 题解

随机坏道

原题链接:洛谷U249552

Limits

时间限制:1000ms

内存限制:256MB

Description

小F有一个磁盘,磁盘上有 n n n个扇区,每个扇区末尾都会有 L , R L,R L,R两个指针。每个指针可以指向一个扇区或者留空。这些扇区恰好形成了一颗二叉树。现在有一个读取程序,它将从 1 1 1号扇区开始读取,读到扇区末时会等概率地任选一个指针进行跳转。如果选择了空指针,那么不会发生跳转,读取程序退出。

有一天,小F发现有一个扇区损坏了。具体而言, k k k号扇区的 L L L指针指向了 m m m号扇区。现在小F想知道,扇区损坏后,读取程序退出前发生跳转次数的数学期望。答案对 998244353 998244353 998244353取模。

Input

第1行, 1 1 1个数 n n n,表示扇区个数;

第2行, n n n个数,依次表示各扇区的尾部的 L L L指针指向的扇区编号, L 1 , L 2 , … , L n L_1,L_2,…,L_n L1,L2,,Ln

第3行, n n n个数,依次表示各扇区的尾部的 R R R指针指向的扇区编号, R 1 , R 2 , … , R n R_1,R_2,…,R_n R1,R2,,Rn

第4行, 2 2 2个数,分别表示 k , m k,m k,m

注意:扇区编号从 1 1 1开始, 0 0 0表示空指针;

Output

一个数,表示所求数学期望对 998244353 998244353 998244353取模的值;

Samples

Sample Input 1
6
2 4 0 0 0 0
3 5 6 0 0 0
2 3
Sample Output 1
124780546

提示:即 15 8 \frac{15}{8} 815

Sample Input 2
6
2 4 0 0 0 0
3 5 6 0 0 0
4 2
Sample Output 2
582309208

提示:即 25 12 \frac{25}{12} 1225

Data range

测试点 n n n特殊性质
1,2 ≤ 500 \le 500 500扇区损坏后一定可以停止读取
3,4 ≤ 500 \le 500 500
5,6 ≤ 1 0 6 \le 10^6 106扇区损坏后一定可以停止读取
7,8,9,10 ≤ 1 0 6 \le 10^6 106

对于100%数据, 1 ≤ k ≤ n ≤ 1 0 6 1\le k\le n\le 10^6 1kn106 0 ≤ L i , R i , m ≤ n 0\le L_i,R_i,m\le n 0Li,Ri,mn

Hints

有理数 a b \frac{a}{b} ba对质数 P P P取模的结果是整数 c c c c c c满足 0 ≤ c < P 0\le c<P 0c<P c ⋅ b ≡ a ( m o d   P ) c \cdot b\equiv a(mod\ P) cba(mod P)

Solution

难度:提高+/省选-

40% 简单递推

记从 x x x号扇区开始读取直到退出时的跳转次数的数学期望为 E ( X ) E(X) E(X),则容易得到递推公式
E ( X ) = { 1 2 E ( L X ) + 1 2 E ( R X ) + 1 , L X ≠ 0 且 R X ≠ 0 1 2 [ E ( L X ) + 1 ] , L X = 0 且 R X ≠ 0 1 2 [ E ( L R ) + 1 ] , L X ≠ 0 且 R X = 0 0 , L X = 0 且 R X = 0 E(X)=\left\{ \begin{aligned} &\frac{1}{2}E(L_X)+\frac{1}{2}E(R_X)+1, L_X \neq 0且R_X \neq 0\\ &\frac{1}{2}\left[E(L_X)+1\right], L_X = 0且R_X \neq 0\\ &\frac{1}{2}\left[E(L_R)+1\right], L_X \neq 0且R_X = 0\\ &0, L_X = 0且R_X = 0 \end{aligned} \right. E(X)= 21E(LX)+21E(RX)+1,LX=0RX=021[E(LX)+1],LX=0RX=021[E(LR)+1],LX=0RX=00,LX=0RX=0

如果我们规定 E ( 0 ) = − 1 E(0)=-1 E(0)=1,则上式可以化简为 E ( X ) = 1 2 E ( L X ) + 1 2 E ( R X ) + 1 E(X)=\frac{1}{2}E(L_X)+\frac{1}{2}E(R_X)+1 E(X)=21E(LX)+21E(RX)+1

对于1,2,5,6四个数据点,直接从底向上(到1号点)递推计算即可。也可以自顶向下递归操作。复杂度 O ( n ) O(n) O(n)

60% 高斯消元

硬盘有 n n n个扇区,则我们可以列出 n n n个线性方程。容易看出可以应用Gauss-Jordan消元法来解方程组,最终得到 E ( 1 ) E(1) E(1)的值。复杂度 O ( n 3 ) O(n^3) O(n3)

该方法可以通过3,4数据点,但有可能卡常数。

100% 递推+拓扑排序

数据范围的特殊条件即描述了一种有向无环图(DaG)的情形。在这种情况下,使用拓扑排序逐步向前递推计算,其复杂度近似是 O ( n ) O(n) O(n)的。

即便不是DaG,我们也容易看出其中最多有一个环。该环一定包含 k k k号扇区,从 k k k号扇区遍历整个图,即可得到整个环,不满足递推性质而需要移项解方程的情形也发生在 k k k号扇区对应的方程中。

因此,我们可以得到最终方法:

  1. 应用拓扑排序,求出部分扇区的期望。如果没有环,这一步就能求出所有扇区的期望;
  2. k k k号扇区开始找环,逐步展开方程,从而求出 k k k号扇区的期望;
  3. 继续拓扑排序,求出所有扇区的期望;

展开方程的方法:

在等式 E ( X ) = 1 2 E ( L X ) + 1 2 E ( R X ) + 1 E(X)=\frac{1}{2}E(L_X)+\frac{1}{2}E(R_X)+1 E(X)=21E(LX)+21E(RX)+1中, L X , R X L_X,R_X LX,RX中有且仅有一个的期望是已知的,因此该式可以改写为 E ( X ) = 1 2 E ( C h X ) + C E(X)=\frac{1}{2}E(Ch_X)+C E(X)=21E(ChX)+C,其中 C h X Ch_X ChX表示X的某个孩子, C C C表示常数。

X = k X=k X=k开始搜索,每次令 X : = C h X X:=Ch_X X:=ChX,直至搜出整个环。

如果环中有 h h h个扇区,则最后的方程为 E ( k ) = ( 1 2 ) h E ( k ) + C ′ E(k)=(\frac{1}{2})^{h}E(k)+C' E(k)=(21)hE(k)+C,可得 E ( k ) = C ′ 1 − 1 2 h E(k)=\frac{C'}{1-\frac{1}{2^h}} E(k)=12h1C

Code

#include<iostream>
#include<vector>
#include<queue>
#include<cassert>
using namespace std;

typedef long long ll;
const ll MOD = 998244353;
const int MAXN = 1000000;
const ll NOT_CEHCKED = -2;

ll E[MAXN + 1];
int deg[MAXN + 1];
pair<int,int> gl[MAXN + 1];
vector<int> gl_rev[MAXN + 1];
bool inque[MAXN + 1];


ll my_pow(ll a, ll x) {
    ll ret = 1;
    while (x) {
        if (x & 1)ret = ret * a % MOD;
        x >>= 1;
        a = a * a % MOD;
    }
    return ret;
}

const ll inv2 = my_pow(2, MOD - 2);

int main()
{
    int n, k, m, cnt = 0;
    //输入
    ios::sync_with_stdio(false);
    cin >> n;
    E[0] = MOD - 1; //-1在模MOD意义下是MOD-1 
    for (int i = 1; i <= n; i++) {
        E[i] = NOT_CEHCKED;  //NOT_CEHCKED表示尚未计算 
		cin >> gl[i].first;
    }
    for (int i = 1; i <= n; i++) {
        cin >> gl[i].second;
    }
    cin >> k >> m; gl[k].first = m;

    //构建反向图用于拓扑排序
    for (int u = 1; u <= n; u++) {
        if (gl[u].first) {
            deg[u]++;
            gl_rev[gl[u].first].push_back(u);
        }
        if (gl[u].second) {
            deg[u]++;
            gl_rev[gl[u].second].push_back(u);
        }
    }

    queue<int> q;
    //叶节点特殊处理
    auto upd_node=[&](int u)->void{
    	for (auto v : gl_rev[u]) {
	        deg[v]--;
	        if (deg[v] == 0 && !inque[v]) {
	            q.push(v);
	            inque[v] = true;
	        }
	    }
	};
    for (int u = 1; u <= n; u++) {
        if (deg[u]) continue;
        q.push(u); inque[u]=true;
    }
    //拓扑排序计算期望
    auto topo_func = [&]()->void {
        while (!q.empty()) {
            int u = q.front(); q.pop(); inque[u] = false;
            //E[u] = (E[uL]+E[uR])/2+1,规定E[0]=-1
            int lch = gl[u].first, rch = gl[u].second;
            E[u] = (((E[lch] + E[rch])%MOD) * inv2 % MOD + 1) % MOD;
            upd_node(u);
        }
    };
    topo_func();
    //已有结果则直接输出
    if (E[1] != NOT_CEHCKED) {
        cout << E[1] << endl;
        return 0;
    }
    //查找环,总是从k号节点开始查找 
    //E[k]=p*E[?]+c
    ll p = inv2, c = (E[gl[k].second] * inv2 + 1) % MOD;
    int unknown = gl[k].first;
    while (unknown != k) {
        int chd_known = gl[unknown].first, chd_unknown = gl[unknown].second;
        if (E[chd_known] == NOT_CEHCKED) {
            swap(chd_known, chd_unknown);
        }
        //assert(E[chd_known] != NOT_CEHCKED && E[chd_unknown] == NOT_CEHCKED);
        ll c0 = (E[chd_known] * inv2 + 1) % MOD;
        c = (c + (p * c0 % MOD)) % MOD;
        p = p * inv2 % MOD;
        unknown = chd_unknown;
    }
    //此时E[k]=p*E[k]+c,E[k]=c/(1-p);
    ll fm = (1 - p + MOD) % MOD;
    E[k] = c * my_pow(fm, MOD - 2) % MOD;
	upd_node(k);
	
    //再次拓扑排序计算
    topo_func();
    cout << E[1] << endl;
    return 0;
}

Postscript

关于分数取模,实际上可以用乘法逆元来描述:
根据费尔马小定理

a p − 1 ≡ 1 ( m o d   p ) , p 为质数, a , p 互质 a^{p-1}≡1(mod\ p), p为质数,a,p互质 ap11(mod p),p为质数,a,p互质

可知

a b ≡ a ⋅ b − 1 ≡ a ⋅ b p − 2 ( m o d   p ) , p 为质数, b , p 互质 \frac{a}{b}\equiv a \cdot b^{-1}\equiv a \cdot b^{p-2}(mod\ p), p为质数,b,p互质 baab1abp2(mod p),p为质数,b,p互质

其他求乘法逆元的方法还有

  1. 线性递推
  2. 拓展欧几里得

请自行学习。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值