JZOJ 5954. 【NOIP2018模擬11.5A組】走向巔峰
阿新 • • 發佈:2019-02-13
題目
給出一棵n個節點的樹,我們每次隨機染黑一個葉子節點(可以重複染黑),操作無限次後,這棵樹的所有葉子節點必然全部會被染成黑色。
定義R為這棵樹不經過黑點的直徑,求使R第一次變小期望的步數。
解題思路
比賽的時候,想到了一個錯誤的方法:去找每條直徑對答案的貢獻,然後點分治尋找直徑的條數。
正解:
尋找套路。尋找直徑R,尋找必經點和必經邊。
考慮R的奇偶性。
如果R為偶,那麼必然有必經點p。
以p為根建樹,則深度為的點才可能成為直徑的端點。
這些點按照它們是以p的哪個兒子為根的子樹來分集合。
如果R為奇,那麼必然有必經邊
則u的子樹的深度為的點為第一個集合,v的子樹的深度為的點為第二個集合。(dep[u]=dep[v]=0)
共2個集合。
必經點,必經邊的存在,是可以證明的。
直徑改變了,當且僅當這些點被刪剩一個集合。
期望怎麼求?
求期望的套路:
①期望的線性性,考慮一個什麼東西對答案的貢獻。(這道題目中不可用)
②順推DP, 不太好弄。
③概率*權值,將總和求出來,然後除以分母。
這道題目中可以用。
考慮剩下哪個集合,這個集合中被刪去了多少個點。
設剩下的集合原本的大小為,刪去了個點。
所有集合的大小之和為,葉子的總數為。
刪除個點的順序共有種。
則對答案的貢獻為:
即
解釋:最後一個點不能夠刪所列舉的集合中的元素。
程式碼
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 500010
#define mo 998244353
#define P(a) putchar(a)
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
struct note{
int to,next;
}edge[N<<1];
int tot,head[N];
int i,j,k,l,n,m,u,v,rt,R;
int dep[N],fa[N],du[N];
int ny[N],Jc[N],Ny[N],cheng[N];
int gs[N],zs,opz,d0,d;
int temp,ans;
bool bz[N];
int read(){
int fh=0,rs=0;char ch=0;
while((ch<'0'||ch>'9')&&(ch^'-'))ch=getchar();
if(ch=='-')fh=1,ch=getchar();
while(ch>='0'&&ch<='9')rs=(rs<<3)+(rs<<1)+(ch^'0'),ch=getchar();
return fh?-rs:rs;
}
void write(int x){
if(x>9)write(x/10);
P(x%10+'0');
}
void lb(int x,int y){
edge[++tot].to=y;
edge[tot].next=head[x];
head[x]=tot;
du[x]++;
}
void dg(int x){
int i;
for(i=head[x];i;i=edge[i].next)
if(fa[x]^edge[i].to){
fa[edge[i].to]=x;
dep[edge[i].to]=dep[x]+1;
dg(edge[i].to);
}
}
void dg1(int x){
int i;
if(dep[x]==(R>>1)){
gs[opz]++;
}
for(i=head[x];i;i=edge[i].next)
if(fa[x]^edge[i].to){
fa[edge[i].to]=x;
if(bz[edge[i].to])continue;
dep[edge[i].to]=dep[x]+1;
dg1(edge[i].to);
}
}
int ksm(int x,int y){
int rs=1;
for(;y;y>>=1,x=(1ll*x*x)%mo)if(y&1)rs=(1ll*rs*x)%mo;
return rs;
}
void pre(){
int i;
fo(i,1,n)ny[i]=ksm(i,mo-2);
Jc[0]=Jc[1]=Ny[0]=Ny[1]=1;
fo(i,2,n)Jc[i]=(1ll*i*Jc[i-1])%mo;
Ny[n]=ksm(Jc[n],mo-2);
fd(i,n-1,2)Ny[i]=(1ll*Ny[i+1]*(i+1))%mo;
}
int C(int n,int m){
return ((1ll*Jc[n]*Ny[m])%mo*Ny[n-m])%mo;
}
int main(){
n=read();
fo(i,1,n-1){
u=read(),v=read();
lb(u,v);lb(v,u);
}
fo(i,1,n){
m=m+(du[i]==1);
if(du[i]>1&&!rt)rt=i;
}
dep[rt]=1;
dg(rt);
rt=0;
fo(i,1,n)if(dep[i]>dep[rt])rt=i;
dep[rt]=0;
fo(i,1,n)fa[i]=0;
dg(rt);
u=0;
fo(i,1,n)if(dep[i]>dep[u])u=i;
R=dep[u];
if(R&1){
fo(i,1,R>>1)u=fa[u];
v=fa[u];
bz[u]=1,bz[v]=1;
opz=1;
fo(i,1,n)fa[i]=0;
dep[u]=0;
dg1(u);
opz=2;
fo(i,1,n)fa[i]=0;
dep[v]=0;
dg1(v);
zs=2;
}else{
fo(i,1,R>>1)u=fa[u];
fo(i,1,n)fa[i]=0;
bz[u]=1;
dep[u]=0;
for(i=head[u];i;i=edge[i].next){
opz++;
dep[edge[i].to]=1;
dg1(edge[i].to);
if(gs[opz]==0)opz--;
}
zs=opz;
}
fo(i,1,zs)d0+=gs[i];
pre();
fo(i,1,n){
cheng[i]=(1ll*m*ny[i])%mo;
cheng[i]=(cheng[i]+cheng[i-1])%mo;
}
fo(j,1,zs){
d=gs[j];
fo(i,0,d-1){
temp=(1ll*C(d,i)*Jc[d0-d+i-1])%mo;
temp=(1ll*temp*(d0-d))%mo;
temp=(1ll*temp*(cheng[d0]-cheng[d-i]+mo)%mo)%mo;
temp=(1ll*temp*Jc[d-i])%mo;
ans=(ans+temp)%mo;
}
}
ans=(1ll*ans*Ny[d0])%mo;
write(ans);
return 0;
}