zoukankan      html  css  js  c++  java
  • JZOJ5954.【NOIP2018模拟11.5A组】走向巅峰

    PROBLEM

    给出一棵n个节点的树,我们每次随机染黑一个叶子节点(可以重复染黑),操作无限次后,这棵树的所有叶子节点必然全部会被染成黑色。
    定义R为这棵树不经过黑点的直径,求使R第一次变小期望的步数。

    前置知识:树的直径的性质、期望的线性性

    如果一棵树有多条直径,当直径长为偶数时,多条直径必定交于一点,当直径长为奇数时,多条直径必定经过同一边,这一点或一边必定在每条直径的中间。

    证明:

    不同的直径必定至少相交于一点(否则这两条直径上的点的路径必定能使直径变长),因此所有直径都相交于一点。

    对于长度为偶数的直径,这个点若不为一条直径的中点,那么从另一条路径经过这个点再前往这条长度大于直径一半的路径,直径就会变长。奇数同理。

    因此我们根据这个中点或这个处于中间的边,可以把所有直径的两端分入若干个集合。如果在染点后只剩下了一个集合,那么直径必定变小。相反,如果有任意两个集合里有点没有被染黑,那么直径依旧不变(请结合图理解)。

    再考虑一下将一个点染黑的期望步数。如果总点数为n,还未染黑的点为m,那么染黑这m个点中任意一个点的期望:

    F=1+(n-m)/n*f

    即1步直接染黑,或染到已经染黑的n-m个点中,再染。

    解方程即可得到f=n/m。

    解决了每染一个点的期望之后,这个问题就转化成了染剩一个集合的期望。

    首先我们考虑那些不在直径上的叶子,它们染或不染都不会对答案造成影响,因此我们假定我们不用染它们,它们对答案的影响仅仅是对f中的n的贡献。

    推公式。枚举染到最后剩下的集合,设这个集合的大小为d,设所有在直径上的叶子总数为d0,我们从0到d-1枚举这个集合最后被染了i个点,注意这个集合在直径变小之前不可能被染完了,不可能枚举到d。因为这样子剩下的集合就不是它。

    公式:

    在这里插入图片描述
    解释:
    从集合中d个中选i个,有组合数C(d,i)。

    染点有顺序,共有(d0-d+i)个点要染,我们要保证最后一个不是这个集合里的点,因提一个此先非该集合的点(d0-d)种出来,(d0-d+i-1)!,最后再把它放进去*(d0-d)。

    从d0个点染到d-i个点,乘上贡献。

    最后因为在操作无线次后所有的点都被染了色,所以剩下d-i个点还有一个染色的顺序。

    最后除以总的方案数,也就是染点的顺序。

    #include<cstdio>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #define maxn 500005
    #define mo 998244353
    #define maxm 1000010
    #define ll long long 
    using namespace std;
    
    int n,m,i,j,k,x,y,dx,dy,dlen;
    int a[maxn],b[maxn],s[maxn];
    int em,e[maxm],nx[maxm],ls[maxn],dep[maxn],fa[maxn],du[maxn];
    ll jc[maxn],leaf,s0,ans,lassum[maxn],tmp;
    
    void read(int &x){
    	x=0; char ch=getchar();
    	for(;ch<'0'||'9'<ch;ch=getchar());
    	for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
    }
    
    void insert(int x,int y){
    	em++; e[em]=y; nx[em]=ls[x]; ls[x]=em;
    	em++; e[em]=x; nx[em]=ls[y]; ls[y]=em;
    }
    
    ll ksm(ll x,ll y){
    	ll s=1;
    	for(;y;y>>=1,x=x*x%mo) if (y&1)
    		(s*=x)%=mo;
    	return s;
    }
    ll C(ll n,ll m){return jc[n]*ksm(jc[n-m]*jc[m]%mo,mo-2)%mo;}
    int dfs(int x,int p){
    	dep[x]=dep[p]+1,fa[x]=p;
    	int mxd1=0,mxd2=0;
    	for(int i=ls[x];i;i=nx[i]) if (e[i]!=p){
    		int tmp=dfs(e[i],x);
    		if (dep[tmp]>dep[mxd1]) mxd2=mxd1,mxd1=tmp;
    		else if (dep[tmp]>dep[mxd2]) mxd2=tmp;
    	}
    	if (mxd1&&mxd2&&dep[mxd1]+dep[mxd2]-dep[x]*2>dlen)
    		dx=mxd1,dy=mxd2,dlen=dep[mxd1]+dep[mxd2]-dep[x]*2;
    	if (mxd1&&dep[mxd1]-dep[x]>dlen)
    		dx=x,dy=mxd1,dlen=dep[mxd1]-dep[x];
    	if (!mxd1) return x; else return mxd1;
    }
    
    void getd(){
    	x=dx,y=dy;
    	if (dep[x]<dep[y]) swap(x,y);
    	while (dep[x]>dep[y]) a[++a[0]]=x,x=fa[x];
    	while (x!=y) a[++a[0]]=x,b[++b[0]]=y,x=fa[x],y=fa[y];
    	a[++a[0]]=x;
    	for(i=b[0];i>=1;i--) a[++a[0]]=b[i];
    }
    
    
    void dfs2(int x,int p,int tp){
    	if (dep[x]==dlen/2) s[tp]++,s0++;
    	for(int i=ls[x];i;i=nx[i]) if (e[i]!=p)
    		dep[e[i]]=dep[x]+1,dfs2(e[i],x,tp);
    }
    
    int main(){
    	freopen("winer.in","r",stdin);
    	freopen("winer.out","w",stdout);
    	read(n);
    	for(i=1;i<n;i++){
    		read(x),read(y);
    		insert(x,y);
    		du[x]++,du[y]++;
    	}
    	leaf=0; for(i=1;i<=n;i++) if (du[i]==1) leaf++;
    	dfs(1,0);
    	getd();
    	if (dlen&1){
    		x=a[a[0]/2],y=a[a[0]/2+1];
    		s[0]=2;
    		dep[x]=0,dfs2(x,y,1);
    		dep[y]=0,dfs2(y,x,2);
    	} else {
    		x=a[a[0]/2+1];
    		dep[x]=0;
    		for(i=ls[x];i;i=nx[i]) dep[e[i]]=1,dfs2(e[i],x,++s[0]);
    	}
    	jc[0]=1,ans=0;
    	for(i=1;i<=s0;i++) jc[i]=jc[i-1]*i%mo;
    	for(i=s0;i>=1;i--) lassum[i]=(lassum[i+1]+leaf*ksm(i,mo-2)%mo)%mo;
    	for(i=1;i<=s[0];i++) for(j=0;j<s[i];j++)
    		(ans+=C(s[i],j)*jc[s0-s[i]+j-1]%mo*(s0-s[i])%mo*lassum[s[i]-j+1]%mo*jc[s[i]-j]%mo*ksm(jc[s0],mo-2)%mo)%=mo;
    	printf("%lld",ans);
    }
    
  • 相关阅读:
    Chrome 29 新功能一览
    7 款免费的 Metro UI 模板
    JPG渐进 & PNG/PNG24 交错测试
    你的钱,以后是放银行还是放支付宝?
    Bise IE6 在你的网站上加上它让IE滚蛋吧
    单例模式常见场景
    10 个最新的开发者工具
    大流量网站的底层系统架构
    DNS解析全过程及原理
    IE条件注释详解
  • 原文地址:https://www.cnblogs.com/DeepThinking/p/11700966.html
Copyright © 2011-2022 走看看