[ABC310G]Takahashi And Pass-The-Ball Game

题目链接

atcoder

洛谷

题目大意

有\(n\)个人,第\(i\)个人有一个目标\(a_i\),和拥有的球的数量\(b_i\)

对于随机的\(x\in\left[1,k\right]\),进行x次以下操作:

  • 第\(i\)个人将自己手中所有的球交给第\(a_i\)个人(所有人同时进行)

求出第\(i\)个人在结束时手里球的个数的期望

对998244353取模

解决方法

容易发现第\(i\)个人在结束时手里球的个数的期望就是对于每个\(x\in\left[1,k\right]\)的第\(i\)个人手里球的个数和除以\(k\)

首先处理除以\(k\)

因为要对998244353取模,所以除以\(k\)就是乘上\(k\)的逆元,用快速幂求即可

然后很容易想到一个暴力做法:

对于第\(i\)个人,枚举他手中的球传递了几次,每次传递后这个人手里球的个数和加上第\(i\)个人手中的球,最后统一除以\(k\)

for(int i=1;i<=n;i++) {
    int now=i;
    for(int j=1;j<=k;j++) {
        now=a[now];
        ans[now]=(ans[now]+b[i])%mod;
    }
}
for(int i=1;i<=n;i++) {
    cout<<ans[i]*invk%mod<<" ";
}

时间复杂度为\(O(nk)\),显然超时

刚才是先枚举第几个人的球,我们还可以先枚举传递了几次

具体来说,就是枚举\(j\),用\(nowa_i\)表示第\(i\)个人手里的球传递了\(j\)次后在谁手上

vector<int>nowa=a;
for(int j=1;j<=k;j++) {
    for(int i=1;i<=n;i++) {
        ans[nowa[i]]=(ans[nowa[i]]+b[i])%mod;
    }
    vector<int>nxtnowa(n+1);
    for(int i=1;i<=n;i++) {
        nxtnowa[i]=a[nowa[i]];
    }
    nowa=nxtnowa;
}
for(int i=1;i<=n;i++) {
    cout<<ans[i]*invk%mod<<" ";
}

时间复杂度仍然为\(O(nk)\)

由于时间主要是\(k\)太大(\(10^{18}\)),可以将其优化为\(O(log_2k)\)的

具体来说是这样的:

我们有\(nowa_i\)表示第\(i\)个人手里的球传递了\(j\)次后在谁手上,我们可以这么改一下定义:

\(nowa_i\)表示第\(i\)个人手里的球传递了\(2^j\)次后在谁手上

初始:

vector<int>nowa=a;

转移:

vector<int>nxtnowa(n+1);
for(int i=1;i<=n;i++) {
    nxtnowa[i]=nowa[nowa[i]];
}
nowa=nxtnowa;

此时仍然套用以上思路,但是直接使用\(b_i\)肯定是不行的,因为\(b_i\)表示传递一次会贡献多少球,需要用\(nowb_i\)表示传递\(2^j\)次会给第\(i\)个人贡献多少球

初始:

vector<int>nowb(n+1);
for(int i=1;i<=n;i++) {
    nowb[a[i]]=(nowb[a[i]]+b[i])%mod;
}

转移:

vector<int>nxtnowb(n+1);
for(int i=1;i<=n;i++) {
    nxtnowb[nowa[i]]=(nxtnowb[nowa[i]]+nowb[i])%mod;
}
for(int i=1;i<=n;i++) {
    nowb[i]=(nowb[i]+nxtnowb[i])%mod;
}

那么就可以用一个类似于快速幂的东西将其优化为\(O(log_2k)\)

vector<int>nowa=a;
vector<int>nowb(n+1);
for(int i=1;i<=n;i++) {
    nowb[a[i]]=(nowb[a[i]]+b[i])%mod;
}
while(k>0) {
    if(k&1) {
        for(int i=1;i<=n;i++) {
            ans[i]=(ans[i]+nowb[i])%mod;
        }
        vector<int>nxtnowb(n+1);
        for(int i=1;i<=n;i++) {
            nxtnowb[nowa[i]]=(nxtnowb[nowa[i]]+nowb[i])%mod;
        }
        nowb=nxtnowb;
    }
    vector<int>nxtnowb(n+1);
    for(int i=1;i<=n;i++) {
        nxtnowb[nowa[i]]=(nxtnowb[nowa[i]]+nowb[i])%mod;
    }
    for(int i=1;i<=n;i++) {
        nowb[i]=(nowb[i]+nxtnowb[i])%mod;
    }
    vector<int>nxtnowa(n+1);
    for(int i=1;i<=n;i++) {
        nxtnowa[i]=nowa[nowa[i]];
    }
    nowa=nxtnowa;
    k>>=1;
}
for(int i=1;i<=n;i++) {
    cout<<ans[i]*invk%mod<<" ";
}

于是我们就能在\(O(nlog_2k)\)的时间内求解

posted on 2026-10-19 08:54  Qinluyan  阅读(2)  评论(0)    收藏  举报