题目大意:
给出一棵树,每条边的长度为,对于原图中每一对距离为的点,连一条长度为的边。
求出的值。


考虑对于每一组的点对,其最短路一定是尽量走新增的边(即加边之前距离为的点对之间的边),当到达距离目标点的长度为时,直接走加边之前的边。这样的话,设在加边之前两个点的距离为,那么最终这两个点之间的距离就是(以下表示向上取整,表示当里面的表达式为真时为,否则为):
表示加边前从的路径长度,将上式带入题目中的式子,得到:,也就是原图上所有点对之间的距离加上长度为奇数的路径的个数的和除以的结果。前者我们可以考虑每条边的边权对最终答案的贡献即可(即路径两端点集大小之积)。
对于后者,我们考虑求路径长度的方法:令表示从根节点到当前节点的路径长度,那么,其中不会对路径长度的奇偶性产生影响,也就是说我们只需要统计奇偶性不同的点对数量就可以得到答案,这部分可以在树形DP的时候顺便记录一下即可。

参考代码:(码风不喜勿喷,建议自行展开代码0^_^0

#include<cstdio>
#define ot e[i].to
struct edge{int to,next;}e[400005];int n,tot=0,last[200005],cnt[2],sz[200005];long long ans=0;
inline void adde(int u,int v) {e[++tot]=(edge){v,last[u]},last[u]=tot;return;}
inline void inse(int u,int v) {adde(u,v),adde(v,u);return;}
void dfs(int root,int fa,int dep) {++cnt[dep^=1],sz[root]=1;//记录下子树大小以及深度奇偶性
    for (register int i=last[root];i;i=e[i].next) if (ot^fa) dfs(ot,root,dep),sz[root]+=sz[ot];//更新子树大小
    return (void)(ans+=1LL*(n-sz[root])*sz[root]);//更新答案
}
int main() {int u,v;
    scanf("%d",&n);for (register int i=1;i<n;++i) scanf("%d%d",&u,&v),inse(u,v);
    dfs(1,0,0);return 0*printf("%lld",ans+1LL*cnt[0]*cnt[1]>>1);//计算答案
}