BZOJ3451 Tyvj1953 Normal

7 篇文章 0 订阅
1 篇文章 0 订阅

由期望的线性性(日常%xuruifan)答案等于每个点的期望被算的次数之和,考虑每个点x会被算多少次,首先最开始肯定会被算一次,而如果另一个点y被选中了,并且x到y的路径上没有别的被选中的点,即y是x到y路径上第一个被选中的点,那么答案会+1

每个点作为路径上第一给被选中的点的概率是一样的,所以答案是sigma 1/dis(i,j)(注意点对是有序的)

所以求出每个长度的路径的条数就可以了

可以点分治+FFT

关于复杂度,我们知道点分治一般有两种写法,一种是对于当前重心,每次搜他的一个子树,把这个子树的信息与已经搜过的子树的信息合并,另一种是直接搜当前分治的子树,更新答案然后搜重心的每个儿子的子树,把不合法的路径取得

这道题如果我们采用第一种方法那么复杂度就是n^2 log^2n 的,而采用第二种方法就是n log^2 n的(真tm玄学,复杂度证明orz ljss)

证明:如果采用第一种方法,对于每层的每个子树,设s为子树大小,FFT的总长度是子树最大深度×根节点儿子个数=O(s^2)的,而如果用第二种方法,对于每层的每个子树,FFT的总长度是最大深度+每个儿子子树的高度=O(s)的,所以第二种方法是n log^2 n而第一种方法是n^2 log n

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<map>
#include<set>
#include<bitset>
#include<queue>
#include<stack>
using namespace std;
#define MAXN 60010
#define MAXM 1010
#define INF 1000000000
#define MOD 1000000007
#define eps 1e-8
#define ll long long
const double pi=acos(-1);
struct vec{
	int to;
	int fro;
};
struct cl{
	double x;
	double y;
	cl(){
		
	}
	cl(double _x,double _y){
		x=_x;
		y=_y;
	}
	friend cl operator +(cl x,cl y){
		return cl(x.x+y.x,x.y+y.y);
	}
	friend cl operator -(cl x,cl y){
		return cl(x.x-y.x,x.y-y.y);
	}
	friend cl operator *(cl x,cl y){
		return cl(x.x*y.x-x.y*y.y,x.y*y.x+x.x*y.y);
	}
	friend cl operator /(cl x,double y){
		return cl(x.x/y,x.y/y);
	}
};
vec mp[MAXN];
int tai[MAXN],cnt;
int siz[MAXN],bal[MAXN];
ll ans[MAXN];
bool vis[MAXN];
int tim[MAXN];
int sum,rt,rtf;
int n;
int m;
int T;
int c[MAXN];
int L,R[MAXN];
cl a[MAXN];
double ANS;
inline void be(int x,int y){
	mp[++cnt].to=y;
	mp[cnt].fro=tai[x];
	tai[x]=cnt;
}
inline void bde(int x,int y){
	be(x,y);
	be(y,x);
}
void getrt(int x,int f){
	int i,y;
	siz[x]=1;
	bal[x]=0;
	for(i=tai[x];i;i=mp[i].fro){
		y=mp[i].to;
		if(!vis[y]&&y!=f){
			getrt(y,x);
			siz[x]+=siz[y];
			bal[x]=max(bal[x],siz[y]);
		}
	}
	bal[x]=max(bal[x],sum-siz[x]);
	if(bal[x]<bal[rt]){
		rt=x;
		rtf=f;
	}
}
void dfs(int x,int f,int d){
	int i,y;
	if(tim[d]!=T){
		c[d]=0;
	}
	c[d]++;
	tim[d]=T;
	m=max(m,d);
	for(i=tai[x];i;i=mp[i].fro){
		y=mp[i].to;
		if(y!=f&&!vis[y]){
			dfs(y,x,d+1);
		}
	}
}
void fft(cl *a,int f){
	int i,j,k;
	for(i=0;i<m;i++){
		if(i<R[i]){
			swap(a[i],a[R[i]]);
		}
	}
	for(i=1;i<m;i<<=1){
		cl wn(cos(pi/i),f*sin(pi/i));
		for(j=0;j<m;j+=(i<<1)){
			cl w(1,0);
			for(k=0;k<i;k++,w=w*wn){
				cl x=a[j+k],y=w*a[j+k+i];
				a[j+k]=x+y;
				a[j+k+i]=x-y;
			}
		}
	}
	if(f==-1){
		for(i=0;i<m;i++){
			a[i]=a[i]/m;
		}
	}
}
void cal(int f){
	int i;
	int M=m<<1;
	L=0;
	for(m=1;m<=M;m<<=1){
		L++;
	}
	for(i=0;i<m;i++){
		R[i]=R[i>>1]>>1|((i&1)<<(L-1));
	}
	for(i=0;i<m;i++){
		if(tim[i]!=T){
			c[i]=0;
		}
		a[i].x=c[i];
		a[i].y=0;
	}
	fft(a,1);
	for(i=0;i<m;i++){
		a[i]=a[i]*a[i];
	}
	fft(a,-1);
	for(i=0;i<min(n,m);i++){
		ans[i]+=(ll)(a[i].x+0.1)*f;
	}
}
void work(int x){
	int i,y;
	vis[x]=1;
	m=0;
	T++;
	dfs(x,0,0);
	cal(1);
	for(i=tai[x];i;i=mp[i].fro){
		y=mp[i].to;
		if(!vis[y]){
			m=0;
			T++;
			dfs(y,0,1);
			cal(-1);
			sum=siz[y];
			rt=0;
			getrt(y,0);
			siz[rtf]=sum-siz[rt];
			work(rt);
		}
	}
}
int main(){
	int i,x,y;
	bal[0]=INF;
	scanf("%d",&n);
	for(i=1;i<n;i++){
		scanf("%d%d",&x,&y);
		x++;
		y++;
		bde(x,y);
	}
	sum=n;
	getrt(1,0);
	siz[rtf]=sum-siz[rt];
	work(rt);
	for(i=0;i<=n-1;i++){
		ANS+=1.0*ans[i]/(i+1);
	}
	printf("%.4lf\n",ANS);
	return 0;
}

/*
5
0 1
1 2
1 3
0 4

3
0 1
1 2

*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值