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

给出一棵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. 快速删除node_modules文件夹

    前言 当安装了较多模块后,node_modules目录下的文件会很多,直接删除整个目录会很慢,下面介绍些快速删除node_modules目录的方法. 方法一:使用rimraf模块的命令 在全局安装ri ...

  2. python操作redis之hash操作

    # __author__ = 'STEVEN' import redis,time #连接池 polls = redis.ConnectionPool(host='192.168.43.22',por ...

  3. 《http和https协议》

    一.HTTP协议 1.官方概念: HTTP协议是Hyper Text Transfer Protocol(超文本传输协议)的缩写,是用于从万维网(WWW:World Wide Web )服务器传输超文 ...

  4. __getitem__,__setitem__,__delitem__

    __getitem__.__setitem__.__delitem__ 总结: __getitem__,__setitem_,__delitem__ : obj[‘属性’]的方式去操作属性时触发的方法 ...

  5. 洛谷 P1042 乒乓球

    P1042 乒乓球 var s:string; a1:array[1..50000] of char; i,n,x,y:longint; procedure f1; begin while not e ...

  6. MySQL之select简单使用

    Select * from table_name Select column_name_1,column_name_2 from table_name Select * from student wh ...

  7. C#中MessageBox用法大全(附效果图)<转>

    我们在程序中经常会用到MessageBox. MessageBox.Show()共有21中重载方法.现将其常见用法总结如下: 1.MessageBox.Show("Hello~~~~&quo ...

  8. IIS+PHP访问量大时内存爆满等性能问题解决方案

    如今还是有许多人在用老掉牙的 IIS6 + PHP.本文解决方法适用于使用 FastCGI 运行 PHP 的用户. 问题原因: 你可以试一试,你在 VPS 上用 IIS6 安装 FastCGI 跑 P ...

  9. gin 打linux环境包问题解决

    打window包直接go build一下,完事, 但是,打linux包出现如下错误 ..\github.com\mattn\go-isatty\isatty_linux.go:7:8: cannot ...

  10. 关于js对象中的,属性的增删改查问题

    删除主要是delet方法: 1 function Person(){}; 2 var person = new Person(); 3 person.name = 'yy'; 4 person.gen ...