Description
给定一棵树,求
∑
i
=
1
n
∑
j
=
i
n
d
i
s
(
i
,
j
)
+
∑
k
=
i
+
1
j
d
i
s
(
k
−
1
,
k
)
(
n
2
)
\dfrac{\sum\limits_{i=1}^n\sum\limits_{j=i}^n{dis(i,j)+\sum\limits_{k=i+1}^j{dis(k-1,k)}}}{\binom{n}{2}}
(2n)i=1∑nj=i∑ndis(i,j)+k=i+1∑jdis(k−1,k)
n
≤
1
0
5
n\le 10^5
n≤105
Solution
恶补计数题
一个比较显然的做法就是我们暴力建虚树,答案就是虚树上所有边的和
我们枚举每条边算贡献,也就是连续一段在两棵子树内都出现的方案数。一个比较巧妙地做法就是将其中一棵子树中的节点在原序列中染黑,那么连续一段同色的不可能成为答案,我们用总的子区间数量减去同色的子区间数量即可,这样n^2做就有60分
考虑用线段树维护同色的长度,我们dfs的时候合并两颗线段树并插入当前节点就可以直接做了。当然启发式合并也是资瓷的
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
typedef long long LL;
const int MOD=1000000007;
const int ny2=500000004;
const int N=200005;
struct edge {int x,y,next;} e[N];
struct treeNode {int l,r,l0,r0,l1,r1; LL sum;} t[N*51];
int rt[N],ls[N],edCnt,tot,n;
LL ans;
int read() {
int x=0,v=1; char ch=getchar();
for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
return x*v;
}
void add_edge(int x,int y) {
e[++edCnt]=(edge) {x,y,ls[x]}; ls[x]=edCnt;
e[++edCnt]=(edge) {y,x,ls[y]}; ls[y]=edCnt;
}
LL f(LL x) {
return x*(x+1)%MOD*ny2%MOD;
}
void push_up(int x,int tl,int tr) {
int ls=t[x].l,rs=t[x].r,mid=(tl+tr)>>1;
if (!ls) {
ls=t[x].l=++tot;
t[ls].l1=t[ls].r1=0;
t[ls].l0=t[ls].r0=mid-tl+1;
t[ls].sum=f(mid-tl+1);
}
if (!rs) {
rs=t[x].r=++tot;
t[rs].l1=t[rs].r1=0;
t[rs].l0=t[rs].r0=tr-mid;
t[rs].sum=f(tr-mid);
}
t[x].sum=t[ls].sum+t[rs].sum;
if (t[ls].r0&&t[rs].l0) {
t[x].sum-=f(t[ls].r0)+f(t[rs].l0);
t[x].sum+=f(t[ls].r0+t[rs].l0);
}
if (t[ls].r1&&t[rs].l1) {
t[x].sum-=f(t[ls].r1)+f(t[rs].l1);
t[x].sum+=f(t[ls].r1+t[rs].l1);
}
t[x].l0=t[ls].l0+((t[ls].l0==mid-tl+1)?(t[rs].l0):0);
t[x].l1=t[ls].l1+((t[ls].l1==mid-tl+1)?(t[rs].l1):0);
t[x].r0=t[rs].r0+((t[rs].r0==tr-mid)?(t[ls].r0):0);
t[x].r1=t[rs].r1+((t[rs].r1==tr-mid)?(t[ls].r1):0);
}
void ins(int &now,int tl,int tr,int x) {
if (!now) now=++tot;
if (tl==tr) {
t[now].sum=t[now].l1=t[now].r1=1;
t[now].l0=t[now].r0=0;
return ;
}
int mid=(tl+tr)>>1;
if (x<=mid) ins(t[now].l,tl,mid,x);
else ins(t[now].r,mid+1,tr,x);
push_up(now,tl,tr);
}
int merge(int x,int y,int tl,int tr) {
if (!(x*y)) return x+y;
int mid=(tl+tr)>>1;
if (t[x].l0==tr-tl+1) return y;
if (t[y].l0==tr-tl+1) return x;
t[x].l=merge(t[x].l,t[y].l,tl,mid);
t[x].r=merge(t[x].r,t[y].r,mid+1,tr);
push_up(x,tl,tr);
return x;
}
void solve(int x,int from) {
for (int i=ls[x];i;i=e[i].next) {
if (e[i].y==from) continue;
solve(e[i].y,x);
rt[x]=merge(rt[x],rt[e[i].y],1,n);
}
ins(rt[x],1,n,x);
ans=(ans+MOD+(f(n)-t[rt[x]].sum)*2LL%MOD)%MOD;
}
LL ksm(LL x,LL dep) {
LL res=1;
for (;dep;dep>>=1) {
(dep&1)?(res=res*x%MOD):0;
x=x*x%MOD;
}
return res;
}
int main(void) {
// freopen("communicate.in","r",stdin);
// freopen("communicate.out","w",stdout);
freopen("data.in","r",stdin);
n=read();
rep(i,2,n) add_edge(read(),read());
solve(1,0);
ans=(ans*ksm(1LL*n*(n+1)%MOD*ny2%MOD,MOD-2)%MOD);
printf("%lld\n", ans);
return 0;
}