正题
题目链接:http://www.51nod.com/Challenge/Problem.html#problemId=2626
题目大意
给出nnn个点的一棵树,每个区间[l,r][l,r][l,r]的代价是选出这个区间中的一个点xxx使得它走到所有点然后又回到xxx的路程最短长度,求一个随机区间的期望代价。
1≤n≤1051\leq n\leq 10^51≤n≤105
解题思路
考虑统计每条边的贡献,一条边会被记入当且仅当分成的两个树各存在一个点在区间中。
考虑怎么统计这个贡献,计在两棵树中的点分别为000和111,那么合法区间就是包含至少一个111和一个000的区间,用线段树统计只包含000或111的区间减去即可。
然后在树上的问题,所以直接上dsu on tree就好了。
时间复杂度:O(nlog2n)O(n\log^2n)O(nlog2n)
然后写完题解突然发现线段树合并好像也行而且更快(?
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1e5+10,P=1e9+7;
struct node{ll to,next;
}a[N<<1];
ll n,tot,ans,ls[N],siz[N],son[N];
ll w[N<<2],l0[N<<2],r0[N<<2],l1[N<<2],r1[N<<2];
void Merge(ll x,ll L,ll R){ll mid=(L+R)>>1;w[x]=w[x*2]+w[x*2+1]+r0[x*2]*l0[x*2+1]+r1[x*2]*l1[x*2+1];l0[x]=(l0[x*2]==mid-L+1)*l0[x*2+1]+l0[x*2];r0[x]=(r0[x*2+1]==R-mid)*r0[x*2]+r0[x*2+1];l1[x]=(l1[x*2]==mid-L+1)*l1[x*2+1]+l1[x*2];r1[x]=(r1[x*2+1]==R-mid)*r1[x*2]+r1[x*2+1];return;
}
void Build(ll x,ll L,ll R){if(L==R){w[x]=r0[x]=l0[x]=1;return;}ll mid=(L+R)>>1;Build(x*2,L,mid);Build(x*2+1,mid+1,R);Merge(x,L,R);return;
}
void Change(ll x,ll L,ll R,ll pos){if(L==R){swap(l0[x],l1[x]);swap(r0[x],r1[x]);return;}ll mid=(L+R)>>1;if(pos<=mid)Change(x*2,L,mid,pos);else Change(x*2+1,mid+1,R,pos);Merge(x,L,R);return;
}
void addl(ll x,ll y){a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;return;
}
void dfs(ll x,ll fa){siz[x]=1;for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(y==fa)continue;dfs(y,x);siz[x]+=siz[y];if(siz[y]>siz[son[x]])son[x]=y;}return;
}
void calc(ll x,ll fa){Change(1,1,n,x);for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(y==fa)continue;calc(y,x);}return;
}
void solve(ll x,ll fa,ll top){for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(y==fa||y==son[x])continue;solve(y,x,y);}if(son[x])solve(son[x],x,top);Change(1,1,n,x);for(ll i=ls[x];i;i=a[i].next){ll y=a[i].to;if(y==fa||y==son[x])continue;calc(y,x);}(ans+=(n*(n+1)/2-w[1])%P)%=P;if(x==top)calc(x,fa);return;
}
ll power(ll x,ll b){ll ans=1;while(b){if(b&1)ans=ans*x%P;x=x*x%P;b>>=1;}return ans;
}
signed main()
{scanf("%lld",&n);for(ll i=1;i<n;i++){ll x,y;scanf("%lld%lld",&x,&y);addl(x,y);addl(y,x);}Build(1,1,n);dfs(1,1);solve(1,1,0);printf("%lld\n",ans*2*power(n*(n+1)/2%P,P-2)%P);return 0;
}