bzoj3451

该博客探讨了一道题目,涉及到在特定条件下点i对答案的贡献,这些条件涉及点分树根的选择。作者指出,当点j作为点分树的根时,i到j之间没有其他点作为树根,这个问题可以转化为卷积问题,并通过快速傅里叶变换(FFT)来解决。尽管作者在写作过程中发现自己总是在第一页进行讨论。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这个题其实就是求



为什么呢?考虑一个点i,枚举每一个j,

他对答案贡献的条件是在j作为点分树根

即i到j之间上没有点被选为点分树根


于是就点分,这个东西就是一个卷积,用fft即可

为什么我一写就是第一页...


#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>
#define inf (1<<30)
#define maxn 30011
using namespace std;
typedef double ldb;
const ldb pi=acos(-1);
const ldb eps=1e-7;
struct edge{
	int r,nxt;
}e[maxn<<1];
struct complex{
	ldb a,b;
	complex(){}
	complex(ldb a,ldb b):a(a),b(b){}
	complex operator+(const complex& c)const{
		return complex(a+c.a,b+c.b);
	}
	complex operator-(const complex& c)const{
		return complex(a-c.a,b-c.b);
	}
	complex operator*(const complex& c)const{
		return complex(a*c.a-b*c.b,a*c.b+b*c.a);
	}
	void clear(){a=b=0;}
}a[maxn],b[maxn];
bool vis[maxn];
int n,head[maxn],esz,size[maxn],num,rt,mn,bh[maxn],len1=0,len2=0,ma[maxn],mb[maxn];
ldb pre[maxn];
ldb ans;
void addedge(int u,int v){
	e[++esz].r=v;e[esz].nxt=head[u];head[u]=esz;
}
void dfs1(int u,int f){
	num++;
	for(int t=head[u];t;t=e[t].nxt)
		if(e[t].r!=f&&!vis[e[t].r])dfs1(e[t].r,u);
}
void dfs2(int u,int f){
	size[u]=1;
	int ans=-inf;
	for(int t=head[u];t;t=e[t].nxt)
		if(e[t].r!=f&&!vis[e[t].r])
			dfs2(e[t].r,u),size[u]+=size[e[t].r],ans=max(ans,size[e[t].r]);
	ans=max(ans,num-size[u]);
	if(ans<mn)mn=ans,rt=u;
}
void findroot(int u){
	num=0;mn=inf;
	dfs1(u,u);dfs2(u,u);
}
void dft(complex h[],int n,int flag){
	for(int i=0;i<n;++i)if(i<bh[i])swap(h[i],h[bh[i]]);
	for(int i=1;i<n;i<<=1){
		complex wn(cos(pi/i),sin(flag*pi/i));
		for(int j=0;j<n;j+=(i<<1)){
			complex w(1,0);
			for(int k=0;k<i;++k){
				complex x=h[j+k];
				complex y=w*h[j+k+i];
				h[j+k]=x+y;
				h[j+k+i]=x-y;
				w=w*wn;
			}
		}
	}
	if(flag==-1)for(int i=0;i<n;++i)h[i].a/=n;
}
void fft(int len1,int x[],int len2,int y[]){
	if(!len1)return ;
	int n=max(len1,len2),m=2*n,k=0;
	for(n=1;n<=m;n<<=1)k++;
	for(int i=0;i<n;++i)bh[i]=0;
	for(int i=0;i<n;++i)bh[i]=((bh[i>>1]>>1)|((i&1)<<(k-1)));
//	putchar(10);
	for(int i=0;i<=n;++i)a[i].clear();
	for(int i=0;i<=n;++i)b[i].clear();
	for(int i=0;i<=len1;++i)a[i].a=x[i];
	
	for(int i=0;i<=len2;++i)b[i].a=y[i];
	
	dft(a,n,1);dft(b,n,1);
	for(int i=0;i<=n;++i)a[i]=a[i]*b[i];
	dft(a,n,-1);
	for(int i=0;i<=m;++i)ans+=pre[i+1]*(int)(a[i].a+0.1);
}
void dfs3(int u,int f,int d){
	mb[d]++;
	ans+=pre[d+1];
	len2=max(len2,d);
	for(int t=head[u];t;t=e[t].nxt)if(e[t].r!=f&&!vis[e[t].r])
		dfs3(e[t].r,u,d+1);
}
void dfs4(int u,int f,int d){
	ma[d]++;mb[d]=0;
	len1=max(len1,d);
	for(int t=head[u];t;t=e[t].nxt)if(e[t].r!=f&&!vis[e[t].r])
		dfs4(e[t].r,u,d+1);
}
void clean(int u,int f,int d){
	ma[d]=mb[d]=0;
	for(int t=head[u];t;t=e[t].nxt)if(e[t].r!=f&&!vis[e[t].r])
		clean(e[t].r,u,d+1);
}
void solve(int u){
	findroot(u);
	vis[rt]=1;
//	printf("[%d]",rt);
	len1=len2=0;
	for(int t=head[rt];t;t=e[t].nxt)if(!vis[e[t].r]){
		dfs3(e[t].r,rt,1);
//		for(int i=1;i<=n;++i)printf("[%d:%d,%d]",i,mb[i],ma[i]);
//		puts("");
		fft(len1,ma,len2,mb);
		dfs4(e[t].r,rt,1);
	}
	clean(rt,rt,0);
	for(int t=head[rt];t;t=e[t].nxt)if(!vis[e[t].r])
		solve(e[t].r);
}
int main(){
//	freopen("in.txt","r",stdin);
	scanf("%d",&n);
	for(int i=1;i<=n;++i)pre[i]=1.0/((double)i);
	for(int i=1,u,v;i<n;++i)
		scanf("%d%d",&u,&v),u++,v++,
		addedge(u,v),addedge(v,u);
	solve(1);
	printf("%.4lf",ans*2+n);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值