[BJOI2014] 想法

[BJOI2014] 想法

有 \(n\) 个集合。对于 \(1 \leq i \leq m\),满足 \(S_i = \{i\}\)。对于 \(m+1 \leq i \leq n\),给定下标 \(x_i,y_i\),满足 \(S_i = S_{x_i} \cup S_{y_i}\)。你需要对于所有 \(m+1 \leq i \leq n\),求出 \(|S_i|\) 的值。你需要在 \(95\%\) 的回答中答案误差不超过 \(25\%\)。

\(1 \leq m \leq 10^5\),\(1 \leq n \leq 10^6\)。


结论 \(1\):在 \([0,1]\) 中随机选择 \(n\) 个实数,\(\min\) 的期望为 \(\dfrac{1}{n+1}\)。

考虑将值域看成环,我们让 \(0,x_1,x_2,\dots,x_n\) 在环上随机分布,此时 \(\min\) 的实际含义就是 \(0\) 后顺时针段的长度。显然会产生 \(n+1\) 段不同的长度,而每一段在含义上都是对等的。也就是说,最终求出的期望就是 \(\dfrac{1}{n+1}\)。


考虑为 \(1\) 到 \(m\) 中的每个集合赋一个随机权值 \(V_i\),对于 \(m+1 \leq i \leq n\),定义 \(V_i = \min(V_{x_i},V_{y_i})\),我们声称期望下 \(|S_i| = \dfrac{1}{V_i} - 1\)。

多跑几次即可通过,时间复杂度 \(O(Tn)\),\(T\) 是随机赋值的次数。

//Ad astra per aspera
#include<iostream>
#include<cstdio>
#include<random>
#include<ctime>
#include<cmath>
using namespace std;
long long rndmax=(1ll<<32)-1;
int x[1000010],y[1000010];
long long num[1000010],ans[1000010];
mt19937 rnd(time(0));
int main(){
	int n,m;
	scanf("%d %d",&n,&m);
	for(int i=m+1;i<=n;i++){
		scanf("%d %d",&x[i],&y[i]);
	}
	int T=200;
	for(int i=1;i<=T;i++){
		for(int j=1;j<=m;j++){
			num[j]=rnd();
		}
		for(int j=m+1;j<=n;j++){
			num[j]=min(num[x[j]],num[y[j]]);
			ans[j]+=num[j];
		}
	}
	for(int i=m+1;i<=n;i++){
		double pre=1.0*ans[i]/T;
		printf("%d\n",int(round(rndmax/pre-1)));
	}
	return 0;
}
posted @ 2026-05-28 16:43  Oken喵~  阅读(17)  评论(0)    收藏  举报