2018.11.17 codechef PRIMEDST(点分治+fft)

传送门
f f t fft fft一眼题(其实最先想到的是 n t t ntt ntt, w a wa wa了几次之后发现模数不够大果断弃疗写 f f t fft fft)
我们点分治统计答案的个数。
考虑现在已经统计出了到当前点的所有距离如何更新答案。
显然如果两个距离能够凑出一个质数的话就对答案有1的贡献。
所以相当于计算出每一个距离的个数。
然后可以推一个式子:
t o t a = ∑ i = 0 m a x d i s c n t i ∗ c n t a − i tot_a=\sum_{i=0}^{maxdis}cnt_i*cnt_{a-i} tota=i=0maxdiscnticntai
这不就是距离数组跟自己卷积吗?
果断 f f t fft fft优化。
然而 f f t fft fft的写法也 w a wa wa了。
d z y o dzyo dzyo拍了半天没错?
然后全局 l o n g l o n g long long longlong了一波终于过了233.
代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
typedef long long ll;
const double pi=acos(-1.0);
const int N=2e5+5;
int n,pri[N],tot=0,msiz[N],siz[N],vis[N],rt,all,tmp[N],lim=1,tim=0,pos[N],maxn;
bool isp[N];
vector<int>e[N>>2];
ll ans=0;
inline void init(){
	isp[1]=1;
	for(register int i=2;i<=n;++i){
		if(!isp[i])pri[++tot]=i;
		for(register int j=1;j<=tot&&i*pri[j]<=n;++j){
			isp[i*pri[j]]=1;
			if(i%pri[j]==0)break;
		}
	}
}
struct Complex{
	double x,y;
	friend inline Complex operator+(const Complex&a,const Complex&b){return (Complex){a.x+b.x,a.y+b.y};}
	friend inline Complex operator-(const Complex&a,const Complex&b){return (Complex){a.x-b.x,a.y-b.y};}
	friend inline Complex operator*(const Complex&a,const Complex&b){return (Complex){a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x};}
	friend inline Complex operator/(const Complex&a,const double&b){return (Complex){a.x/b,a.y/b};}
}cnt[N];
inline void fft(Complex a[],int type){
	for(register int i=0;i<lim;++i)if(i<pos[i])swap(a[i],a[pos[i]]);
	for(register int mid=1;mid<lim;mid<<=1){
		Complex wn=(Complex){cos(pi/mid),sin(type*pi/mid)};
		for(register int j=0,len=mid<<1;j<lim;j+=len){
			Complex w=(Complex){1,0};
			for(register int k=0;k<mid;++k,w=w*wn){
				Complex a0=a[j+k],a1=a[j+k+mid]*w;
				a[j+k]=a0+a1,a[j+k+mid]=a0-a1;
			}
		}
	}
	if(type==-1)for(register int i=0;i<lim;++i)a[i]=a[i]/(double)lim;
}
void getroot(int p,int fa){
	msiz[p]=siz[p]=1;
	for(register int i=0;i<e[p].size();++i){
		int v=e[p][i];
		if(v==fa||vis[v])continue;
		getroot(v,p),siz[p]+=siz[v],msiz[p]=max(msiz[p],siz[v]);
	}
	msiz[p]=max(msiz[p],all-siz[p]);
	if(msiz[p]<msiz[rt])rt=p;
}
void getdis(int p,int fa,int delt){
	cnt[delt].x+=1;
	maxn=max(maxn,delt);
	for(register int i=0;i<e[p].size();++i){
		int v=e[p][i];
		if(vis[v]||v==fa)continue;
		getdis(v,p,delt+1);
	}
}
inline void calc(int p,int delt,int type){
	maxn=0,getdis(p,0,delt),lim=1,tim=0;
	while(lim<=maxn*2)++tim,lim<<=1;
	for(register int i=0;i<lim;++i)pos[i]=(pos[i>>1]>>1)|((i&1)<<(tim-1));
	int sum=(int)-cnt[1].x;
	fft(cnt,1);
	for(register int i=0;i<lim;++i)cnt[i]=cnt[i]*cnt[i];
	fft(cnt,-1);
	for(register int i=1;i<=tot;++i){
		if(pri[i]>lim)break;
		sum+=(int)(cnt[pri[i]].x+0.5);
	}
	ans+=(ll)sum/2*type;
	for(register int i=0;i<lim;++i)cnt[i].x=cnt[i].y=0;
}
void solve(int p){
	calc(p,0,1),vis[p]=1;
	for(register int i=0;i<e[p].size();++i){
		int v=e[p][i];
		if(vis[v])continue;
		all=siz[v],rt=0,calc(v,1,-1),getroot(v,0),solve(rt);
	}
}
signed main(){
	freopen("lx.in","r",stdin);
	n=read(),init();
	for(register int i=1,u,v;i<n;++i)u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
	all=msiz[rt=0]=n,getroot(1,0),solve(rt);
	printf("%.8lf",(double)ans*2.0/(double)((double)n*(double)(n-1)));
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值