3105 小明喜欢树 V2
小明非常喜欢树,有一天小明得到这样一棵树,每个树节点都有一个编号,有n个节点的树的编号为1到n,每个编号代表该节点的海拔高度,现在小明要在这颗树上找到一些路径,从起点到终点需要满足海拔先单调上升后单调下降的性质,起点或终点不同即为不同的路径,问满足条件的路径有多少条,聪明的你可以帮助小明解决这个问题吗?
输入
第一行输入一个整数n(1 <= n <= 300000)
接下来n-1行,每行有两个整数,表示这两个节点连在一起
输出
输出路径的个数。
数据范围
对于10%的数据,1<= n <= 8
对于50%的数据,1 <= n <= 1024
对于100%的数据,1 <= n <= 300000
输入样例
5
1 5
4 5
2 4
3 4
输出样例
8
样例解释
符合条件的路径有
1-5-4
1-5-4-3
1-5-4-2
3-4-2
另外四条路径反过来即可,一共八条路径。
解析:
假如我们知道,以节点 ii 为起点的单调递减的路径数量Dp[i],那么从这些路径中,任选2条不在同一棵子树下的,便可以快速计算出,有多少条先升后降的路径以该节点为最高点。
以上面的示例为例:节点 5 包括 4 条单调递减的路径,因此以 5 为最高点的路径数量为 1×3+3×1=6。
有了节点对应的 Dp[i]Dp[i],便可以在O(n)时间内计算出最终的结果。
那么如何快速计算Dp[i]呢?考虑使用树形Dp。
我们可以利用一次 DFSDFS 求出所有的Dp[i],这个DFS有2个阶段,第一阶段,只处理编号比自己小的子节点,状态转移是从子节点到父节点。
经过第一阶段处理之后,统计出了所有来自子节点的递减路径。这部分的处理需要先递归再累加。
在DFS的第二阶段,只处理编号比自己大的子节点,并将当前节点的计数,累加到子节点,状态转移从父节点到子节点。
经过第二阶段处理之后,统计出了所有来自父节点的递减路径。这部分的处理需要先累加再递归,与第一阶段正好相反。
有了这两部分的累加计数,我们再调用最初阶段所说的统计方法,就可以快速计算出所有符合条件的路径数量。
以样例为例,求得的Dp数组如下:
Dp[5]=4,Dp[4]=2,Dp[1]=Dp[2]=Dp[3]=0。
放代码:
#include<bits/stdc++.h>
using namespace std;
const int N=1<<10;
inline char nc()
{
static char buf[N],*p1=buf,*p2=buf;
return p1==p2&&(p2=(p1=buf)+fread(buf,1,N,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline int scan(T &x)
{
char c=nc();
if(c==EOF)return EOF;
for(x=0;isdigit(c);c=nc())x=x*10+(c^48);
return x;
}
template<typename T, typename...Args>
inline int scan(T &x, Args&...args)
{
return scan(x),scan(args...);
}
struct Edge
{
int to,next;
Edge() {}
Edge(int a,int b):to(a),next(b) {}
}E[600005];
int tot=0,head[300005];
long long ans=0,DP[300005]={0};
void add(int x,int y)
{
E[++tot]=Edge(y,head[x]);
head[x]=tot;
E[++tot]=Edge(x,head[y]);
head[y]=tot;
}
void DFS1(int x,int fa)
{
int i,j;
for(i=head[x];i;i=E[i].next)
{
j=E[i].to;
if(j==fa)continue;
DFS1(j,x);
if(j<x)DP[x]+=DP[j]+1;
}
}
void DFS2(int x,int fa)
{
int i,j;
long long s=0;
for(i=head[x];i;i=E[i].next)
{
j=E[i].to;
if(j<x)ans+=s*(DP[j]+1),s+=DP[j]+1;
if(j==fa)continue;
if(j>x)DP[j]+=DP[x]+1;
DFS2(j,x);
}
}
int main()
{
int i,j,n;
scan(n),n--;
while(n--)scan(i),scan(j),add(i,j);
DFS1(1,0),DFS2(1,0);
printf("%lld\n",ans<<1);
return 0;
}