【Atcoder】 [ARC150D] Removing Gacha

题目链接

点击打开链接

题目解法

考虑期望的线性性, A n s = ∑ i = 1 n E ( X i ) Ans=\sum\limits_{i=1}^{n}E(X_i) Ans=i=1nE(Xi)
其中 E ( X i ) E(X_i) E(Xi) 为选 i i i 的期望次数
考虑点 i i i 有关的点只有它的祖先
因为我们当前求的是 i i i 的期望次数,和它祖先选的次数没有关系,所以我们可以考虑忽略 “它或它的任意一个祖先未被选中” 的条件
所以将问题转化成了有 d e p t h i depth_i depthi 个点,每次随机抽一个点,知道所有点都被抽过(否则之后 i i i 也可能被抽),求第 d e p t h i depth_i depthi 个点被抽的期望次数
考虑当前剩下了 k k k 个点,下一个抽到之前未抽过的点的概率是 d e p t h i − k d e p t h i \frac{depth_i-k}{depth_i} depthidepthik,所以期望步数为 d e p t h i d e p t h i − k \frac{depth_i}{depth_i-k} depthikdepthi
所以抽完 d e p t h i depth_i depthi 个点的期望步数为 ∑ k = 0 d e p t h i − 1 d e p t h i d e p t h i − k \sum\limits_{k=0}^{depth_i-1}\frac{depth_i}{depth_i-k} k=0depthi1depthikdepthi
所以第 i i i 个点被抽中的期望次数即为 ∑ k = 0 d e p t h i − 1 1 d e p t h i − k = ∑ k = 1 d e p t h i 1 k \sum\limits_{k=0}^{depth_i-1}\frac{1}{depth_i-k}=\sum\limits_{k=1}^{depth_i}\frac{1}{k} k=0depthi1depthik1=k=1depthik1
直接计算即可
时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

#include 
using namespace std;
const int N=200100,P=998244353;
int n,d[N];
int e[N<<1],ne[N<<1],h[N],idx;
inline int read(){
	int FF=0,RR=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
	for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
	return FF*RR;
}
void add(int x,int y){ e[idx]=y,ne[idx]=h[x],h[x]=idx++;}
void dfs(int u){
	for(int i=h[u];~i;i=ne[i]) d[e[i]]=d[u]+1,dfs(e[i]);
}
int qmi(int a,int b){
	int res=1;
	for(;b;b>>=1){
		if(b&1) res=1ll*res*a%P;
		a=1ll*a*a%P;
	}
	return res;
}
int main(){
	memset(h,-1,sizeof(h));
	n=read();
	for(int i=2;i<=n;i++){
		int fa=read();
		add(fa,i);
	}
	d[1]=1,dfs(1);
	h[1]=1;
	for(int i=2;i<=n;i++) h[i]=(h[i-1]+qmi(i,P-2))%P;
	int ans=0;
	for(int i=1;i<=n;i++) ans=(ans+h[d[i]])%P;
	printf("%d\n",ans);
	fprintf(stderr,"%d ms\n",int(1e3*clock()/CLOCKS_PER_SEC));
	return 0;
}

你可能感兴趣的:(Atcoder,算法)