链接:
https://www.51nod.com/onlineJudge/questionCode.html#problemId=1803¬iceId=396652
树是随机生成的。
就题目的代码设计思路不会很复杂。但问题是树的深度较坏情况的概率。我们是需要分析好的。
也就是说,我们需要明白随机生成一棵树的期望深度是多少。出现稍微坏一点的可能又是多少。
题目中生成树的方式可以生成任何一种形状的
n
节点树。
这是因为树也是DAG 图.任何形状的树,都可以拓扑排序后重新标号。
我们计算这种方式生成的树的期望深度。其实就是随机生成一棵树的期望深度。
但是平均不代表不能代表特殊。
更具体来说,对于
n
个节点的随机树。最大深度超过h 时候大概率是多少?(是否有绝对的概率优势)。
这里只能给出平均意义下的树的深度。出现教坏情况的概率应该不会很大。
首先。第
n
个结点可能深度是多少呢?
算法可以看作我们从节点n 开始.每次随机的选择一个之前的节点。去跳跃。每次跳跃距离都不超过到达节点
1
的距离的一半概率近似为O(12)
连续
k
次跳跃。出现这种情况的概率显然随着k 的增加收敛很快。
O((12)k)
。但这个分析很不严谨。能力有限。不能给出准确的概率分布。
但可以看出。出现坏的情况的概率不会很大。
那么平均意义下的结论还是值得使用的。
定义。节点
n
的平均深度为 f(n)
则:
f(n)=1n−1∑k=1n−1(f(k)+1)=1+1n−1∑k=1n−1f(k)
则:
f(n)(n−1)=(n−1)+∑k=1n−1f(k)f(n−1)(n−2)=(n−2)+∑k=1n−2f(k)
所以:
f(n)(n−1)−f(n−1)(n−2)=1+f(n−1)f(n)(n−1)=f(n−1)(n−1)+1f(n)=f(n−1)+1n−1
所以:
f(n)=Hn−1,n>1
特别的: f(1)=0
其中
Hn=O(logen)
现在。我们就当作这是一个深度为 O(log) 的树。
提供一种反向离线递推的方法:
对于问题。离线存储后。按照
l
排序。
令s(l,r) 为:询问
l,r
时对应的答案。
显然: s(l,r)≤s(l,r+1)s(l,r)≤s(l−1,r)
定义
dp[i][k]
表示:以从
i
开始。最小的使得s(l,r)≥k 的
r
定义:q(P,l,r) 表示从
P
出发。只使用[l,r] 的边的最长路径。
定义
len[P][k]
表示:以点
P
为出发点。最小的使得q(P,i,r)≥k 的
r
考虑左侧加入一条边时。新加入的边的起点产生的影响:
只可能与其兄弟之间产生了化学变化( - - ! )
而新加入边后更新了父节点的len 需要
O(log2n)
的时间。
所以总时间复杂度时 O(nlog2n)
始终想不明白题解的 O(nlog n)
还有:此题输入数据量过大。建议使用分块读取。
下面是AC代码:
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include <cmath>
#define MAXN 500005
#define CHILD 1000
using namespace std;
const int INF=0x3f3f3f3f;
struct Io
{
char A[MAXN],*L,*R;
Io()
{
L=R=A;
}
void Io_fread()
{
L=A;
R=A+fread(A,sizeof(char),MAXN,stdin);
}
void read(int &tmp)
{
tmp=0;
if(L==R)
{
Io_fread();
if(L==R)return;
}
while(*L<'0'||*L>'9')
{
L++;
if(L==R)
{
Io_fread();
if(L==R)return;
}
}
while(*L>='0'&&*L<='9')
{
tmp=tmp*10+*L-'0';
L++;
if(L==R)
{
Io_fread();
if(L==R)return;
}
}
}
}I;
struct prob
{
int l,r;
prob(){};
bool operator <(const prob&a)const
{
return l>a.l;
}
}Q[MAXN];
int len[MAXN][61];
int ph[MAXN];
int dp[CHILD];
int main ()
{
freopen("/Users/gaoxusheng/Desktop/In.txt","rw",stdin);
int n,a,b,c=0,ans=0,q;
I.read(n);
for(int i=1;i<n;i++)
{
I.read(a);
I.read(b);
ph[b]=a;
}
I.read(q);
for(int i=0;i<q;i++)
{
I.read(Q[i].l);
I.read(Q[i].r);
Q[i].l++; Q[i].r++;
if(Q[i].l>n)Q[i].l=n;
if(Q[i].r>n)Q[i].r=n;
}
sort(Q,Q+q);
memset(len,0x3f,sizeof len);
memset(dp,0x3f,sizeof dp);
for(int d=n;d;d--)
{
int P=ph[d];
len[d][0]=d;
len[P][0]=d;
dp[1]=d;
for(int k=0;len[d][k]<INF;k++)
for(int t=0;len[P][t]<INF;t++)
{
int u;
if(len[d][k]>len[P][t])
u=len[d][k];
else
u=len[P][t];
if(dp[k+t+1]>u)dp[k+t+1]=u;
}
for(int k=0;len[d][k]<INF;k++)
if(len[P][k+1]>len[d][k])
len[P][k+1]=len[d][k];
while(c<q&&Q[c].l>=d)
{
int u=0;
for(int k=1;dp[k]<=Q[c].r;k++)u=k;
ans+=u;
c++;
}
}
printf("%d\n",ans);
return 0;
}