bzoj 3451: Tyvj1953 Normal

只想到了要单独考虑每个点的贡献
如果x是x到y路径上第一个被删除的点,那么对答案有1的贡献
所以总共的答案就是所有点对的1/dis(x,y)注意dis(x,x)只算一遍
这个可以用点分治+fft解决。
   
   
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
 
#define ll long long
#define inf 1e9
#define eps 1e-8
#define md 998244353
#define N 300010
using namespace std;
struct yts { int x,t,ne;} e[N];
int v[N],son[N],sz[N],rev[N],dep[N],mx_dep,now_dep,p[N],q[N];
bool vis[N];
ll a[N],b[N],c[N],f[N],mi[N];
int sum,root,num;
 
void put(int x,int y)
{
num++; e[num].x=x; e[num].t=y;
e[num].ne=v[x]; v[x]=num;
}
 
ll kpow(ll a,int b,int p)
{
ll ans=1;
while (b)
{
if (b&1) ans=ans*a%p;
a=a*a%p; b>>=1;
}
return ans%p;
}
 
void dft(ll a[],int len,int n)
{
for (int i=1;i<n;i++) if (i<rev[i]) swap(a[i],a[rev[i]]);
for (int k=0;k<len;k++)
{
int m=1<<k; ll g=mi[n/(m*2)];
for (int i=0;i<n;i+=2*m)
{
ll cg=1;
for (int j=i;j<i+m;j++)
{
ll &A=a[j+m],&B=a[j],t=A*cg%md;
A=(B-t+md)%md; B=(B+t)%md; cg=cg*g%md;
}
}
}
}
 
void fft(int s)
{
int len=0,n=1;
while (n<=s) { n<<=1; len++;}
for (int i=0;i<n;i++) a[i]=p[i],b[i]=q[i];
for (int i=1;i<n;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(len-1));
ll cg=kpow(3,(md-1)/n,md);
mi[0]=1; for (int i=1;i<n;i++) mi[i]=mi[i-1]*cg%md;
dft(a,len,n); dft(b,len,n);
for (int i=0;i<n;i++) c[i]=a[i]*b[i]%md;
reverse(mi+1,mi+n);
dft(c,len,n);
ll ni=kpow(n,md-2,md);
for (int i=0;i<n;i++) { c[i]=c[i]*ni%md; f[i]+=c[i];}
}
 
void get_root(int x,int fa)
{
sz[x]=1; son[x]=0;
for (int i=v[x];i;i=e[i].ne)
{
int y=e[i].t;
if (!vis[y]&&y!=fa)
{
get_root(y,x);
sz[x]+=sz[y];
son[x]=max(son[x],sz[y]);
}
}
son[x]=max(son[x],sum-sz[x]);
if (son[x]<son[root]) root=x;
}
 
void get_dep(int x,int fa)
{
dep[x]=dep[fa]+1; q[dep[x]]++; now_dep=max(now_dep,dep[x]);
for (int i=v[x];i;i=e[i].ne)
{
int y=e[i].t;
if (y!=fa&&!vis[y]) get_dep(y,x);
}
}
 
void work(int x)
{
vis[x]=1; dep[x]=0; p[0]=1;
mx_dep=0;
for (int i=v[x];i;i=e[i].ne)
{
int y=e[i].t;
if (!vis[y])
{
now_dep=0;
get_dep(y,x);
fft(mx_dep+now_dep);
for (int j=0;j<=now_dep;j++) p[j]+=q[j],q[j]=0;
mx_dep=max(mx_dep,now_dep);
}
}
for (int j=0;j<=mx_dep;j++) p[j]=0;
for (int i=v[x];i;i=e[i].ne)
{
int y=e[i].t;
if (!vis[y])
{
sum=sz[y]; root=0; get_root(y,0); work(root);
}
}
}
 
int main()
{
int n;
scanf("%d",&n);
for (int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
x++; y++;
put(x,y); put(y,x);
}
son[0]=n; sum=n; root=0; get_root(1,0); work(root);
//for (int i=0;i<=10;i++) printf("%lld ",f[i]); printf("\n");
double ans=0;
for (int i=1;i<=2*n;i++) ans+=f[i]*1.0/(i+1);
printf("%.4lf\n",ans*2+n);
return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值