SDSC2018 Day2

本文详细解析了最近公共祖先(LCA)问题,并通过一个具体的编程实例进行讲解。介绍了如何利用数学公式推导出最优解,给出了针对不同数据规模的优化思路,最后附上了完整的C++代码实现。

$ \Rightarrow $ 戳我下载文件密码:apia  

T1数学害死人啊,只写了T1要完蛋啦!!!w(゚Д゚)w
  ## Problem A. 最近公共祖先 (commonants.c/cpp/pas)

Input file: commonants.in Output file: commonants.out Time Limit : 0.5 seconds Memory Limit: 512 megabytes  

最近公共祖先(Lowest Common Ancestor,LCA)是指在一个树中同时拥有给定的两个点作为后 代的最深的节点。

为了学习最近公共祖先,你得到了一个层数为 $ n + 1 $ 的满二叉树,其中根节点的深度为 $ 0 $ ,其他 节点的深度为父节点的深度 $ +1 $ 。 你需要求出二叉树上所有点对 $ (i,j) $ ,$ (i,j $ 可以相等,也可以 $ i > j)$ 的最近公共祖先的深度之和对 $ 10^9 + 7 $ 取模后的结果。  

Input

一行一个整数 $ n $ 。  

Output

一行一个整数表示所有点对 $ (i,j),(i,j $ 可以相等,也可以 $ i > j)$ 的最近公共祖先的深度之和对 $ 10^9 + 7 $ 取模后的结果。  

Examples

commonants.in|commonants.out --|-- 2|22 19260817|108973412  

Notes

样例 1 解释:

pic

树一共有 7 个节点(一个根节点和两个子节点), 其中 (4,4),(5,5),(6,6),(7,7) 共 4 对的最近公共祖先深度为 2, (4,2),(2,4),(5,2),(2,5),(5,4),(4,5),(2,2),(6,3),(3,6),(3,7),(7,3),(6,7),(7,6),(3,3) 共 14 对最 近公共祖先深度是 1 , 其他的点对最近公共祖先深度为 0 ,所以答案为 22 。

对于 20% 的数据,$ n ≤ 10 $ 。 对于 50% 的数据,$ n ≤ 106 $ 。 对于 100% 的数据,$ 1 ≤ n ≤ 109 $ 。

 

题解

  • 考虑每棵子树对答案的贡献。

  • 设当前层数为 $ i $ ,子树的大小(节点数量,包括子树根)为 $ 2^{n-i+1} $ ,该层有 $ 2^i $ 棵这样的子树。

  • 对于除子树根外,两棵子树的节点互相选择,$ lca$ 一定为子树根,贡献为 $ \frac{2^{n-i+1}-1}{2} \times \frac {2^{n-i+1}-1}{2} \times 2 $

  • 对于子树根来说,每个子节点与自己的 $ lca $ 一定也是自己,贡献为 $ (2^{n-i+1}-1) \times 2 \times i $

  • 对于整棵子树来说,自己和自己的 $ lca $ 也要贡献答案,所以设上述贡献的和为 $ tmp $ ,则这层的贡献为 $ (tmp+i)*2^i $

  • 整个式子是这样的 $ \sum_{i=1}n ((\frac{2{n-i+1}-1}{2} \times \frac {2^{n-i+1}-1}{2} \times 2 + (2^{n-i+1}-1) \times 2 \times i )+i)*2^i $  

  • 化简(这个过程很艰难)得:$ \sum_{i=1}n (2{2n-i+1}-2^i) \times i $

  • 好的,熟悉的数学必修5时间到了!ヾ(•ω•`)o  

$$ 2S=2^{2n+1} - 2^2+ 2 \times (2^{2n}-2^3) + 3 \times (2^{2n-1} - 2^4) + \dots + (n-1) \times (2^{n+2} - 2^n) + n \times (2^{n+2} - 2^{n+1}) $$   - 展开一、二式   $$ S=2^{2n} - 2 \times 2^2 + 2\times 2^{2n-1} - 3 \times 2^3 + 3 \times 2^{2n-2} + \dots + n \times 2^{n+1} - n \times 2^n - 2^1 $$ $$ 2S=2 \times 2^{2n} - 2^2 + 3 \times 2^{2n-1} - 2 \times 2^3 + 4 \times 2^{2n-2} - \dots + n \times 2^{n+2} - n \times 2^{n+1} + 2^{2n+1} $$   - 合并(这个过程也很艰难),我们可以发现式子就是(可能有误,未效验)   $$ 2S-S= \sum_{i=1}^n 2^i + \sum_{i=n+1}^{2n} 2^i - n \times 2^{n+1} $$   - 最终的答案为:   $$ ans = 2^{2n+2} + 2^{n+1} - (n+1) \times 2^{n+2} -2 $$   - 直接使用快速幂即可   ### 代码 ```cpp #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define int unsigned long long #define Mod 1000000007 int n,tot,ans; int qpow(int k){ int res=2,x=2; --k; while(k){ if(k&1) res=res*x%Mod; x=x*x%Mod; k>>=1; } return res; } signed main(){ freopen("commonants.in","r",stdin); freopen("commonants.out","w",stdout); scanf("%lld",&n); printf("%lld",((((qpow(2*n+2)%Mod+qpow(n+1)%Mod)%Mod-(n+1)*qpow(n+2)%Mod)+Mod)%Mod-2+Mod)%Mod); return 0; } ```

转载于:https://www.cnblogs.com/PotremZ/p/SDSC2018Day2.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值