倍增与位运算有很多共性;这题做法有一点像「线段树上二分」和「线段树套二分」的关系。

给出一棵n个点的树(以1号点为根),定义dep[i]为点i到根路径上点的个数。众所周知,树上最近公共祖先问题可以用倍增算法解决。现在我们需要算出这个算法精确的复杂度。我们定义计算点i和点j最近公共组先的精确复杂度为bit[dep[i]-dep[lca(i,j)]]+bit[dep[j]-dep[lca(i,j)]](bit[i]表示i在二进制表示下有多少个1,lca(i,j)表示点i和点j的最近公共祖先)。为了计算平均所需的复杂度为多少,请你帮忙计算任意两点计算最近公共组先所需复杂度的总和。

即计算 ∑n−1i=1∑nj=i+1 bit[dep[i]-dep[lca(i,j)]]+bit[dep[j]-dep[lca(i,j)]]

Input

第一行一个数n表示点数(1<=n<=100,000)
接下来n-1行每行两个数x,y表示一条边(1<=x,y<=n)

Output

一个数表示答案

Input示例

4
1 2
1 3
2 4

Output示例

8

题目分析

题目已经良心地把要求的式子给出来了。

自上向下的方法一

位运算计数题自然而然考虑按位计算贡献,注意到要求的是bit即个数,也就是说高位和低位是同性的。而这题略有特殊的是在于可以与倍增相结合做一些有趣的事情。

由于边权为1,倍增预处理的到祖先节点的二的幂次,就是到祖先节点的距离的二进制拆分。

那么就可以先枚举logn数量的每一种2^i深度d,再枚举所有n个点。对于每一个枚举的点,统计与它相距2^{d-1}<dis≤2^d的祖先节点的贡献。

直观来说就是这张图。主要代码是这样的:

     for (int d=; d<; d++)
{
memset(w, , sizeof w);
for (int ix=; ix<=n; ix++)
{
int i = chain[ix];    //树的dfs序,因为要自上向下做
w[i] = tot[f[i][d+]]-tot[f[i][d]];
if (dep[i] > <<(d+)) w[i] += w[fa[f[i][d+]]];
ans += w[i];
}
}

如果想要更加程式化的描述,见51Nod1709 复杂度分析这篇博客。

 #include<bits/stdc++.h>
const int maxn = ; int n,w[maxn];
long long ans;
int chain[maxn],tot[maxn],chTot;
int f[maxn][],dep[maxn],fa[maxn];
int edgeTot,edges[maxn<<],nxt[maxn<<],head[maxn]; int read()
{
char ch = getchar();
int num = ;
bool fl = ;
for (; !isdigit(ch); ch = getchar())
if (ch=='-') fl = ;
for (; isdigit(ch); ch = getchar())
num = (num<<)+(num<<)+ch-;
if (fl) num = -num;
return num;
}
void addedge(int u, int v)
{
edges[++edgeTot] = v, nxt[edgeTot] = head[u], head[u] = edgeTot;
edges[++edgeTot] = u, nxt[edgeTot] = head[v], head[v] = edgeTot;
}
void dfs(int x, int fat)
{
f[x][] = x, f[x][] = fa[x] = fat, dep[x] = dep[fat]+;
tot[x] = , chain[++chTot] = x;
for (int i=head[x]; i!=-; i=nxt[i])
{
int v = edges[i];
if (v!=fat){
dfs(v, x);
tot[x] += tot[v];
}
}
}
int main()
{
memset(head, -, sizeof head);
n = read();
for (int i=; i<n; i++) addedge(read(), read());
dfs(, );
for (int j=; j<=; j++)
for (int i=; i<=n; i++)
f[i][j] = fa[f[f[i][j-]][j-]];
for (int d=; d<; d++)
{
memset(w, , sizeof w);
for (int ix=; ix<=n; ix++)
{
int i = chain[ix];
w[i] = tot[f[i][d+]]-tot[f[i][d]];
if (dep[i] > <<(d+)) w[i] += w[fa[f[i][d+]]];
ans += w[i];
}
}
printf("%lld\n",ans);
return ;
}

自上向下的方法二

在这里给出一种O(nlog)的做法。记录每个节点的fa和子树size。
枚举一个k,用s[i]表示节点i子树内和i距离小于 2^k 大于等于2^k−1的节点数,v[i]表示节点i子树内和i距离小于 2^k 大于等于 2^k−1 的节点和i距离的bit总数,now[i]表示i向上的第 2^k−1 个祖先。
每次倍增计算每个点子树内和它距离小于 2^k 大于等于 2^k−1 的点的距离对答案的贡献即可。

@hzq84621  自http://www.51nod.com/question/index.html#!questionId=1546

【博客园抽风没法正常显示数学公式我也很难受啊】

END

【树论 倍增】51nod1709 复杂度分析的更多相关文章

  1. [51nod1709]复杂度分析

    给出一棵n个点的树(以1号点为根),定义dep[i]为点i到根路径上点的个数.众所周知,树上最近公共祖先问题可以用倍增算法解决.现在我们需要算出这个算法精确的复杂度.我们定义计算点i和点j最近公共组先 ...

  2. 51nod1709复杂度分析

    题解: 注意到,如果第j位有贡献,那么从i往上跳2^j,然后不能再跳超过2^j. 因此可以考虑倍增. 代码: #include<bits/stdc++.h> typedef long lo ...

  3. CodeVs.2370 小机房的树 ( LCA 倍增 最近公共祖先)

    CodeVs.2370 小机房的树 ( LCA 倍增 最近公共祖先) 题意分析 小机房有棵焕狗种的树,树上有N个节点,节点标号为0到N-1,有两只虫子名叫飘狗和大吉狗,分居在两个不同的节点上.有一天, ...

  4. 数据结构( Pyhon 语言描述 ) — — 第3章:搜索、排序和复杂度分析

    评估算法的性能 评价标准 正确性 可读性和易维护性 运行时间性能 空间性能(内存) 度量算法的运行时间 示例 """ Print the running times fo ...

  5. bzoj5089 最大连续子段和 分块+复杂度分析+凸包

    题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=5089 题解 本来打算迟一点再写这个题解的,还有一个小问题没有弄清楚. 不过先写一下存个档吧. ...

  6. [HNOI2016]树(可持久化线段树+树上倍增)

    [HNOI2016]树(可持久化线段树+树上倍增) 题面 给出一棵n个点的模板树和大树,根为1,初始的时候大树和模板树相同.接下来操作m次,每次从模板树里取出一棵子树,把它作为新树里节点y的儿子.操作 ...

  7. 51nod 1709 复杂度分析

    51nod 1709 复杂度分析 考虑定义 $ F(x) $ 为 \(x\) 为根的子树所有点与 $ x $ 的深度差(其实就是 $ x $ 到每个子树内点的距离)的 1 的个数和. 注意,$ F(x ...

  8. 分布式机器学习:同步并行SGD算法的实现与复杂度分析(PySpark)

    1 分布式机器学习概述 大规模机器学习训练常面临计算量大.训练数据大(单机存不下).模型规模大的问题,对此分布式机器学习是一个很好的解决方案. 1)对于计算量大的问题,分布式多机并行运算可以基本解决. ...

  9. 相似度分析,循环读入文件(加入了HanLP,算法第四版的库)

    相似度分析的,其中的分词可以采用HanLP即可: http://www.open-open.com/lib/view/open1421978002609.htm /****************** ...

随机推荐

  1. PostgreSQL - 怎么转换数据类型

    前言 对于select 233;这个sql,得到的结果是int4类型,如果我们希望将结果转换成其他的数据类型,有以下方法(下边的{数据类型}表示占位符,要替换成数据库中的某一种数据类型): 方法一:使 ...

  2. 10M光纤与下载速度KB/s、MB/s的换算

    我们经常听说谁开的宽带是4M或10M或20M等等.那这和我们所说的网速“多少MB/s”.“多少KB/s”等同吗? 其实这不是一个概念. 在宽带运营商那里开的宽带,比如4M,其实是说4M bit/s,即 ...

  3. IP服务-3-DHCP

    DHCP代表了动态IP地址分配的下一阶段.DHCP建立在BOOTP协议格式的基础上,专注于动态分配多种信息,以及为未来的扩展提供灵活的消息结构,并且无需预先定义每个客户端的MAC地址.DHCP提供的功 ...

  4. java操作mongodb数据库实现新建数据库,新建集合,新建文档

    *首先明确一点,要通过java代码创建mongodb数据库实例,需要同时创建集合和文档. 代码实现: /* 建立与mongodb数据库的连接,可指定参数,如:MongoClient client = ...

  5. 整理一下 通知传值 Block传值

    Block: 一. (1) 在需要传值的界面定义属性 // 点击collectionViewCell的回调 @property (nonatomic, copy) void(^Didcollectio ...

  6. [題解] luogu p1220 關路燈

    區間dp 题目描述 某一村庄在一条路线上安装了n盏路灯,每盏灯的功率有大有小(即同一段时间内消耗的电量有多有少).老张就住在这条路中间某一路灯旁,他有一项工作就是每天早上天亮时一盏一盏地关掉这些路灯. ...

  7. JMeter中的HTTPS套接字错误

    Apache JMeter对启用SSL的应用程序执行性能和/或负载测试时,SSL套接字错误可能是经常遇到的麻烦,严重阻碍了您的测试工作.本文重点介绍如何通过相应地配置和调优JMeter来克服这些与连接 ...

  8. SqlServer存储过程调用接口

    因项目需求.需实现新增数据->触发器->存储过程->调用接口. https://blog.csdn.net/u010485134/article/details/58603370 另 ...

  9. Technocup 2017 - Elimination Round 1 (Unofficially Open for Everyone, Rated for Div. 2) B

    Vasily exited from a store and now he wants to recheck the total price of all purchases in his bill. ...

  10. Serega and Fun Codeforces - 455D || queue

    https://codeforces.com/problemset/problem/455/D 其实方法很多,然而当初一个也想不到... 1.分块,块内用链表维护 修改[l,r]就当成删除第r个元素, ...