1. 程式人生 > >【NOIP2017提高A組衝刺11.2】救贖(數學期望)

【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;
}