「NOIP2018模拟9.18」最近公共祖先 - 结论题

无原题地址

最近公共祖先 (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\)) 的最近公共祖先的深度之和对 \(1e9 + 7\) 取模后的结果。

Input

一行一个整数 \(n\)

Output

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

Examples

sample 1 input
2
sample 2 input
19260817
sample 1 output
22
sample 2 output
108973412

Notes

对于 \(20\)% 的数据,\(n ≤ 10\)
对于 \(50\)% 的数据,\(n ≤ 106\)
对于 \(100\)% 的数据,\(1 ≤ n ≤ 109\)
样例 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\)

思路

结论题阿我整整推了一个小时阿我的青春阿阿阿阿
观察这道题,能看出是跟二叉树有关的,所以我们就能联想到二的幂数(不要问我怎么联想到的)
我一开始其实是想试图通过计算每个点对点对(断句:点 对 点对)的贡献来找规律的,结果失败了(可能因为鄙人经验欠缺吧)
于是便枚举了一下\(n\)分别等于\(1、2、3\)时对每一层的做贡献的点对个数
ieE2s1.png
从以上表格不难得出以下式子

\(ans=\sum_{i=1}^N (2^{2n-i+1}+2^i)\times i\)

然而我推到这之后卡了半个小时=】
但是我并没有放弃 并且x绞尽脑汁想出了一个玄学的东西XD
那就是 错位相减法
接下来是推导:

\(S = 2^{2n}+2\times2^{2n-1}+3\times2^{2n-2}+...+n\times2^{n+1}-n\times2^n-...-2\times2^2-2^1\)

\(2S = 2^{2n+1}+2\times2^{2n}+3\times2^{2n-1}+...+n\times2^{n+2}-n\times2^{n+1}-(n-1)\times2^n-...-3\times2^2-2\times2^2\)

\(2S-S = 2^{2n+1}+2^{2n}+...+2^{n+2}-2^{n+1}\times2n+2^n+...+2^2+2^1\)

\(S = 2^{2n+1}+2^{2n}+...+2^{n+2}-2^{n+1}\times2n+2^n+...+2^2+2^1+1-1+2^{n+1}-2^{n+1}\)

\(S = 2^{2n+2}-1-2^{n+1}\times(2n+1)-1\)

\(S = 4^{n+1}-2^{n+1}\times(2n+1)-2\)

代码

#include<cstdio>
#include<cctype>
#define rg register
#define int long long 
using namespace std;
inline int read(){
    rg int f=0,x=0;
    rg char ch=getchar();
    while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
    while(isdigit(ch))  x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    return f?-x:x;
}

const int mod =1e9+7;
int ans,n;
inline int power(int a,int b,int p){
    int ans=1;
    for(;b;b>>=1){
        if(b&1) ans=ans*a%p;
        a=a*a%p;
    }
    return ans;
}
signed main(){
    freopen("commonants.in","r",stdin);
    freopen("commonants.out","w",stdout);
    n=read();
    int tmp1=power(4,n+1,mod);
    int tmp2=power(2,n+1,mod)*(2*n+1)+2;
    while(tmp1<=tmp2)   tmp1+=mod;
    ans=(tmp1-tmp2)%mod;
    printf("%lld",ans);
    return 0;
}

转载于:https://www.cnblogs.com/horrigue/p/9669847.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值