SDSC2018 Day2
$ \Rightarrow $ 戳我下载文件密码:apia
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 解释:
树一共有 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 ≤ 10^6 $ 。
对于 100% 的数据,$ 1 ≤ n ≤ 10^9 $ 。
题解
-
考虑每棵子树对答案的贡献。
-
设当前层数为 $ 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}-2i) \times i $
-
好的,熟悉的数学必修5时间到了!ヾ(•ω•`)o
- 展开一、二式
- 合并(这个过程也很艰难),我们可以发现式子就是(可能有误,未效验)
- 最终的答案为:
- 直接使用快速幂即可
代码
#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;
}