洛谷 P6154 游走

洛谷 P6154 游走

洛谷传送门

题目背景

zbw 在 B 城游走。

题目描述

B 城可以看作一个有 nn 个点 mm 条边的有向无环图可能存在重边

zbw 在 B 城随机游走,他会随机选择一条路径,选择所有路径的概率相等。路径的起点和终点可以相同。

定义一条路径的长度为经过的边数,你需要求出 zbw 走的路径长度的期望,答案对 998244353998244353 取模。

输入格式

第一行两个整数 n,mn,m

接下来 mm 行,每行两个整数 x,yx,y,表示存在一条从 xx 到 yy 的有向边。

输出格式

一行一个整数,表示答案对 998244353998244353 取模后的值。


题解:

暴力思路:

从每个点开始全图遍历,累加出所有路径条数和路径长度和,两者相除求逆元即是答案。

开Longlong。

目标40pts,实测 pts。

代码:

#include<cstdio>
#define int long long
using namespace std;
const int maxn=1e5+10;
const int maxm=7*1e5+10;
const int tt=5*1e6;
const int mod=998244353;
int n,m,cnt,tmp;
int tot,to[maxm],nxt[maxm],head[maxn];
int inv[tt];
void add(int x,int y)
{
    to[++tot]=y;
    nxt[tot]=head[x];
    head[x]=tot;
}
void dfs(int x,int dep)
{
    cnt++;
    tmp+=dep;
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        dfs(y,dep+1);
    }
}
signed main()
{
    scanf("%lld%lld",&n,&m);
    inv[1]=1;
    for(int i=2;i<=tt;i++)
        inv[i]=((mod-mod/i)*inv[mod%i])%mod;
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%lld%lld",&x,&y);
        add(x,y);
    }
    for(int i=1;i<=n;i++)
        dfs(i,0);
    int ans=(tmp*inv[cnt])%mod;
    printf("%lld
",ans);
    return 0;
}

正解记忆化?本来以为是什么期望DP+拓排之类的小神题(当然对于学弟来讲是水题)。结果正解就是暴搜+记忆化优化。

怀疑人生。

逆元没有必要拿线性求逆元,因为998244353是质数,所以逆元直接就是(a^{p-2}),快速幂即可。

代码:

#include<cstdio>
#define int long long
using namespace std;
const int maxn=1e5+10;
const int maxm=7*1e5+10;
const int tt=5*1e6;
const int mod=998244353;
int n,m,cnt,tmp,ans;
int tot,to[maxm],nxt[maxm],head[maxn];
int f[maxn],g[maxn];
//f[i]表示以i为起点的路径条数,g[i]表示以i为起点的路径总长
void add(int x,int y)
{
    to[++tot]=y;
    nxt[tot]=head[x];
    head[x]=tot;
}
void dfs(int x)
{
    if(f[x])
        return;
    f[x]=1;
    for(int i=head[x];i;i=nxt[i])
    {
        int y=to[i];
        dfs(y);
        f[x]=(f[x]+f[y])%mod;
        g[x]=(g[x]+f[y]+g[y])%mod;
    }
}
int qpow(int a,int b)
{
    int ret=1;
    while(b)
    {
        if(b&1)
            ret=(ret*a)%mod;
        b>>=1;
        a=(a*a)%mod;
    }
    return ret%mod;
}
signed main()
{
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%lld%lld",&x,&y);
        add(x,y);
    }
    for(int i=1;i<=n;i++)
        if(!f[i])
            dfs(i);
    for(int i=1;i<=n;i++)
    {
        cnt=(cnt+f[i])%mod;
        tmp=(tmp+g[i])%mod;
    }
    ans=(tmp%mod*qpow(cnt,mod-2)%mod)%mod;
    printf("%lld
",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/fusiwei/p/13844723.html