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=1sizej∗Pedge(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;
}