1. 程式人生 > >JZOJ 5954. 【NOIP2018模擬11.5A組】走向巔峰

JZOJ 5954. 【NOIP2018模擬11.5A組】走向巔峰

題目

給出一棵n個節點的樹,我們每次隨機染黑一個葉子節點(可以重複染黑),操作無限次後,這棵樹的所有葉子節點必然全部會被染成黑色。
定義R為這棵樹不經過黑點的直徑,求使R第一次變小期望的步數。

解題思路

比賽的時候,想到了一個錯誤的方法:去找每條直徑對答案的貢獻,然後點分治尋找直徑的條數。
正解:
尋找套路。尋找直徑R,尋找必經點和必經邊。
考慮R的奇偶性。
如果R為偶,那麼必然有必經點p。
以p為根建樹,則深度為R2\frac{R}{2}的點才可能成為直徑的端點。
這些點按照它們是以p的哪個兒子為根的子樹來分集合。
如果R為奇,那麼必然有必經邊(u,v)(u,v)
則u的子樹的深度為R

12\frac{R-1}{2}的點為第一個集合,v的子樹的深度為R12\frac{R-1}{2}的點為第二個集合。(dep[u]=dep[v]=0)
共2個集合。
必經點,必經邊的存在,是可以證明的。
直徑改變了,當且僅當這些點被刪剩一個集合。
期望怎麼求?
求期望的套路:
①期望的線性性,考慮一個什麼東西對答案的貢獻。(這道題目中不可用)
②順推DP,f[i]=(pjf[j])f[i]=\sum(p_j*f[j]) 不太好弄。
③概率*權值,將總和求出來,然後除以分母。
這道題目中可以用。
考慮剩下哪個集合,這個集合中被刪去了多少個點。
設剩下的集合原本的大小為d
d
,刪去了ii個點。
所有集合的大小之和為d0d_0,葉子的總數為mm
刪除d0d_0個點的順序共有d0!d_0!種。
則對答案的貢獻為:
i=0d1PE\sum_{i=0}^{d-1}P*E

i=0d1Cdi(d0d+i1)!(d0d)(di)!d0!j=di+1d0mj\sum_{i=0}^{d-1}\frac{C_d^i*(d_0-d+i-1)!*(d_0-d)*(d-i)!}{d_0!}*\sum_{j=d-i+1}^{d_0}\frac{m}{j}
解釋:最後一個點不能夠刪所列舉的集合中的元素。

程式碼

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