1. 程式人生 > >JZOJ 5444. 【NOIP2017提高A組衝刺11.2】救贖

JZOJ 5444. 【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。

Solution

  • 利用期望的線性既可以得出對於一個有根樹的答案是,複雜度 O

    (N2)

    i=1n(sizei1)(soni1)soni
  • 但是可以發現換一次根許多點的貢獻都不變,

  • 那麼我們先跑出以 1 為根的答案,每次 O(1) 換根後,更改相鄰的邊的貢獻即可。

  • 而且我們可以 O(N) 預處理出 1N 的逆元,詳細方法和證明見下面連結:

  • 這樣時間複雜度就成了大常數的 O(N)

Code

#include<cstdio>
using namespace std;
typedef long long LL;
const int N=1e5+1,mo=998244353;
int n,tot;
int first[N],next[N<<1
],en[N<<1]; int fa[N],size[N],son[N]; LL ans[N],inv[N]; inline int read() { int X=0,w=1; char ch=0; while(ch<'0' || ch>'9') {if(ch=='-') w=-1;ch=getchar();} while(ch>='0' && ch<='9') X=(X<<3)+(X<<1)+ch-'0',ch=getchar(); return X*w; } inline void write(int x) { if(x>9) write(x/10); putchar(x%10+'0'); } inline void insert(int x,int y) { next[++tot]=first[x]; first[x]=tot; en[tot]=y; } inline void dfs(int x) { size[x]=1,son[x]=0; for(int i=first[x];i;i=next[i]) if(en[i]!=fa[x]) { fa[en[i]]=x; son[x]++; dfs(en[i]); size[x]+=size[en[i]]; } } inline void find(int x) { if(fa[x]) ans[1]=(ans[1]+(son[fa[x]]-1)*inv[son[fa[x]]]%mo*size[x])%mo; for(int i=first[x];i;i=next[i]) if(en[i]!=fa[x]) find(en[i]); } inline void work(int x) { int y=fa[x];LL sum=0; if(son[y]) sum=(sum+mo-(n-1)*(son[y]-1)*inv[son[y]]%mo)%mo; if(son[x]) sum=(sum+mo-(size[x]-1)*(son[x]-1)%mo*inv[son[x]]%mo)%mo; fa[y]=x,fa[x]=0; son[x]++,son[y]--; int z=size[x];size[x]=n,size[y]-=z; if(son[x]) sum=(sum+(n-1)*(son[x]-1)%mo*inv[son[x]]%mo)%mo; if(son[y]) sum=(sum+(size[y]-1)*(son[y]-1)%mo*inv[son[y]]%mo)%mo; ans[x]=(ans[y]+sum)%mo; for(int i=first[x];i;i=next[i]) if(en[i]!=y) work(en[i]); fa[x]=y,fa[y]=0; son[x]--,son[y]++; size[x]=z,size[y]=n; } int main() { n=read(); for(int i=1;i<n;i++) { int x=read(),y=read(); insert(x,y); insert(y,x); } inv[0]=inv[1]=1; for(int i=2;i<=n;i++) inv[i]=(mo-mo/i)*inv[mo%i]%mo; dfs(1),find(1); for(int i=first[1];i;i=next[i]) work(en[i]); for(int i=1;i<=n;i++) write(ans[i]),putchar('\n'); return 0; }