\(\mathcal{Description}\)

  Link.

  给定一棵 \(n\) 个点的树,求无序三元组 \((u,v,w)\) 的个数,满足其中任意两点树上距离相等。

  \(n\le10^5\)。

\(\mathcal{Solution}\)

  考虑如何计数。对于任意三元组 \((u,v,w)\),我们仅在其两两路径所进过的树上最高点对其统计一次。如图:

  对于三元组 \((4,6,7)\),我们仅希望在 \(1\) 处统计它的贡献。

  考虑 DP,记 \(d(u,v)\) 表示 \(u\) 到 \(v\) 的树上距离。令 \(f(u,i)\) 表示 \(u\) 子树内 \(v\) 的个数,满足 \(d(u,v)=i\);\(g(u,i)\) 表示 \(u\) 子树内无序二元组 \((p,q)\) 的个数,满足 \(d(p,\operatorname{lca}(p,q))=d(q,\operatorname{lca}(p,q))=d(\operatorname{lca}(p,q),u)+i\)。例如上图的 \(g(2,2)=1\),无序二元组 \((4,6)\) 满足条件。

  如此设计状态的意义在于,在 \(g(u,i)\) 的基础上,在 \(u\) 子树的外部接上一个满足 \(d(u,w)=i\) 的 \(w\) 就能构成三元组,并且三元组恰好在最高点 \(u\) 计数。

  暴力转移比较显然,发现状态的第二维的范围均为 \(u\) 向下的最长链长,所以用长链剖分优化,直接移动指针继承 \(\mathcal O(1)\) 继承长儿子信息,做到均摊 \(\mathcal O(n)\) 转移,故总复杂度 \(\mathcal O(n)\)。

\(\mathcal{Code}\)

#include <cstdio>

#define alloc( u ) \
( f[u] = cur, g[u] = ( cur += dep[u] << 1 ), cur += dep[u] << 1 ) typedef long long LL; const int MAXN = 1e5;
int n, ecnt, head[MAXN + 5];
int dep[MAXN + 5], son[MAXN + 5];
LL ans, *f[MAXN + 5], *g[MAXN + 5], pool[MAXN * 5], *cur = pool; struct Edge { int to, nxt; } graph[MAXN * 2 + 5]; inline void link ( const int s, const int t ) {
graph[++ ecnt] = { t, head[s] };
head[s] = ecnt;
} inline void init ( const int u, const int fa ) {
for ( int i = head[u], v; i; i = graph[i].nxt ) {
if ( ( v = graph[i].to ) ^ fa ) {
init ( v, u );
if ( dep[v] > dep[son[u]] ) son[u] = v;
}
}
dep[u] = dep[son[u]] + 1;
} inline void solve ( const int u, const int fa ) {
if ( son[u] ) {
f[son[u]] = f[u] + 1, g[son[u]] = g[u] - 1;
solve ( son[u], u );
}
f[u][0] = 1, ans += g[u][0];
for ( int i = head[u], v; i; i = graph[i].nxt ) {
if ( ( v = graph[i].to ) ^ fa && v ^ son[u] ) {
alloc ( v ), solve ( v, u );
for ( int j = 0; j < dep[v]; ++ j ) {
if ( j ) ans += f[u][j - 1] * g[v][j];
ans += g[u][j + 1] * f[v][j];
}
for ( int j = 0; j < dep[v]; ++ j ) {
g[u][j + 1] += f[u][j + 1] * f[v][j];
if ( j ) g[u][j - 1] += g[v][j];
f[u][j + 1] += f[v][j];
}
}
}
} int main () {
scanf ( "%d", &n );
for ( int i = 1, u, v; i < n; ++ i ) {
scanf ( "%d %d", &u, &v );
link ( u, v ), link ( v, u );
}
init ( 1, 0 );
alloc ( 1 );
solve ( 1, 0 );
printf ( "%lld\n", ans );
return 0;
}

最新文章

  1. Struts的拦截器
  2. 【转】JavaScript中的原型和继承
  3. C# ArrayList的用法
  4. LabVIEW之生产者/消费者模式--队列操作 彭会锋
  5. three.js阴影
  6. android firmware 利用UDP socket发送Magic Packet--c语言版本
  7. MySql5.7-多源复制(多主单从)
  8. C++11变长参数模板
  9. delphi CoolBar这个怎么弄没了
  10. js 类似发微博或者微信朋友圈的时间显示 刚刚 几天前
  11. sed 入门
  12. INSTALL_FAILED_NO_MATCHING_ABIS
  13. poj_3261_Milk Patterns(后缀数组)
  14. nodejs构建多房间简易聊天室
  15. 为什么阿里的程序员那么帅?---原来他们都有&quot;编码规约扫描&quot;神器在手
  16. 01 Android修改新建虚拟机存放的位置
  17. drawer
  18. linux下目录简介——/sys
  19. Luogu P4479 [BJWC2018]第k大斜率
  20. SpringBoot-@async异步执行方法

热门文章

  1. gitlab新增ssh
  2. react中自定义antd主题与支持less(第二部)
  3. iview在ie9及以上的兼容问题解决方案
  4. Vue下路由History mode 出现404,无法正常刷新
  5. HDU 1009 FatMouse' Trade (贪心)
  6. AGC041F Histogram Rooks
  7. 《剑指offer》面试题27. 二叉树的镜像
  8. k8s-storage-class
  9. 将Cesium ion上的3D Tiles和Bing imagery应用到osgEarth
  10. 论文解读第三代GCN《 Deep Embedding for CUnsupervisedlustering Analysis》