【NOIP2017提高A组冲刺11.2】救赎(数学期望)

Description

“是的。”我回答,“我不会忘记你。在森林里我会一点点记起往日的世界。要记起的大概很多很多:各种人、各种场所、各种光、各种歌曲……”
——村上春树《世界尽头与冷酷仙境》

在没有心存在的世界尽头,音乐能够使小镇居民消散的心重新聚拢成形。作为镇子里唯一一个还残留着些许音乐记忆的人,我逐渐记起了往昔点滴……

记忆中有一棵无根树,有n个节点。
对于一棵有根树的每一个非叶子节点,我们都等概率选中其一个儿子节点作为偏好儿子。对于一条从父亲指向儿子的树边(u,v),如果v是u的偏好儿子,则称这条边为重边,否则为轻边。
我们定义一棵有根树的权值为其每一个节点到根路径上的轻边条数的和的期望值。
请对无根树每一个节点输出其为根的有根树的权值。答案模998244353。

Input

文件第一行是一个正整数n。
接下来n-1行,每行两个正整数(x,y)表示一条树边。

Output

输出文件共n行,每一行一个整数表示答案。

Sample Input

5
1 2
1 3
3 4
3 5

Sample Output

3
1
665496238
499122178
499122178

Data Constraint

对于10%的数据,保证n<=10。
对于30%的数据,保证n<=2000。
对于100%的数据,保证n<=10^5。

题解

这个题的实现和天天爱跑步有的一拼。 - - - - 一位大佬在赛后如此说道。
首先,我们通过思考,可以想出来 O(n2) 的做法。
dpi=nj=1sizejPedge(on the path of i->j)
然后我们来思考如何优化。
我们考虑枚举每条边,计算贡献。
我们发现,一条边对一个点的贡献只有两种值,分别对应这个点在这条边的两侧。
所以,我们在用上面的方程计算出贡献之后,我们只需要分别将两边的点加上贡献即可。
我们可以发现,这两类点,一类其实就是那条边的子树内的所有点,另一类就是其他点。
所以我们只需要随便选一个点进行一遍dfs,得到所需信息,以及dfs序,就可以做了。
修改时差分地在dfs序上修改即可。
然后发现这样做都过不了样例,重新手玩样例之后,发现其实有两个点是特殊的,就是边的两个端点。
它们的值还要另外计算。
这个题其实就是不好调试。
代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#define ll long long
using namespace std;
inline int read(){
    int x=0;char ch=' ';int f=1;
    while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    if(ch=='-')f=-1,ch=getchar();
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
const int N=1e5+5;
const int p=998244353;
struct edge{int to,next;}e[N<<1];
int n,tot;
int head[N],dfn_clock,mp[N];
ll inv[N],ans[N],all,du[N],size[N];
inline void addedge(int x,int y){e[++tot].to=y;e[tot].next=head[x];head[x]=tot;}
inline ll frac(int x,int y){return (x*inv[y])%p;}
inline void getinv(){
    inv[0]=inv[1]=1;
    for(int i=2;i<=n;i++)inv[i]=((p-p/i)*inv[p%i])%p;
}
inline void dfs(int x,int fa){
    size[x]=1;mp[x]=++dfn_clock;
    for(int i=head[x];i;i=e[i].next){
        int u=e[i].to;
        if(u==fa)continue;
        dfs(u,x);
        size[x]+=size[u];
    }
}
inline void dfs2(int x,int fa){
    for(int i=head[x];i;i=e[i].next){
        int u=e[i].to;
        if(u==fa)continue;
        ll sizeu=size[u],dux=du[x],duu=du[u],mpx=mp[x],mpu=mp[u];
        ll num=(sizeu*frac(max(dux-2,0LL),dux-1))%p;
        all+=num;if(all>p)all%=p;
        ans[mpu]-=num;
        ans[mpu+sizeu]+=num;
        ans[mpx]-=num;
        ans[mpx+1]+=num;
        num=(sizeu*frac(dux-1,dux))%p;
        ans[mpx]+=num;
        ans[mpx+1]-=num;

        num=((n-sizeu)*frac(max(duu-2,0LL),duu-1))%p;
        ans[mpu]+=num;
        ans[mpu+sizeu]-=num;
        ans[mpu]-=num;
        ans[mpu+1]+=num;
        num=((n-sizeu)*frac(duu-1,duu))%p;
        ans[mpu]+=num;
        ans[mpu+1]-=num;
        dfs2(u,x);
    }
}
int main(){
    freopen("redemption.in","r",stdin);
    freopen("redemption.out","w",stdout);
    n=read();
    getinv();
    for(int i=1;i<n;i++){
        int x=read(),y=read();
        addedge(x,y);addedge(y,x);
        du[x]++;du[y]++;
    }
    dfs(1,0);
    dfs2(1,0);
    ans[0]=all;
    for(int i=1;i<=n;i++){
        ans[i]=(ans[i-1]+ans[i])%p;
    }
    for(int i=1;i<=n;i++){
        printf("%lld\n",(ans[mp[i]]+p)%p);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值