【题解】牛客OI周赛1-提高组 B.树 树上倍增+组合计数

链接:https://www.nowcoder.com/acm/contest/199/B
来源:牛客网
【题解】牛客OI周赛1-提高组 B.树 树上倍增+组合计数
【题解】牛客OI周赛1-提高组 B.树 树上倍增+组合计数


学习了大佬代码。对着这份代码看了一个多小时好像有点点明白。大概就是在这头选两个端点在那头选两个端点更新答案(说了等于没说)。选子树内那一头是不能在同一个子节点的子树内部选两个(会多占一些边),选子树外那头也得把共了边的部分减去,保证最后往上跳之后求出来的路径组合长度是在范围内的。

#include<cstdio>
#include<cstring>
const int N=1e5+10,mod=1e9+7;
int hd[N],tot,n,sz[N],dep[N],fa[N][20],a[N],l,r,b[N],ans;
struct Edge{
	int v,nx;
}e[N<<1];
int C2(int x){return 1ll*x*(x-1)/2%mod;}//返回C(x,2) 
void add(int u,int v)
{
	e[tot].v=v;
	e[tot].nx=hd[u];
	hd[u]=tot++;
}
void dfs(int u)
{
	sz[u]=1;
	for(int i=1;(1<<i)<=dep[u];i++)fa[u][i]=fa[fa[u][i-1]][i-1];
	for(int i=hd[u];~i;i=e[i].nx)
	{
		int v=e[i].v;
		if(v==fa[u][0])continue;
		fa[v][0]=u;dep[v]=dep[u]+1;dfs(v);
		sz[u]+=sz[v];a[u]=(a[u]+C2(sz[v]))%mod;
	}
	a[u]=(C2(sz[u])-a[u]+mod)%mod;//在u的子树中取2个不共路径的端点 
} 
void Dfs(int u)
{
	for(int i=hd[u];~i;i=e[i].nx)
	{
		int v=e[i].v;
		if(fa[u][0]==v)continue;
		b[v]=(0ll+b[u]+a[u]-C2(sz[u])+mod-C2(n-sz[u])+mod+C2(sz[v])+C2(n-sz[v]))%mod;
	    //在v的子树外取两个不共路径的端点 
		Dfs(v);
	}
}
int up(int u,int d)
{
	for(int i=19;i>=0;i--)if(d&(1<<i))u=fa[u][i];
	return u;
}
int main()
{
	//freopen("in.txt","r",stdin);
    memset(hd,-1,sizeof(hd));
    scanf("%d%d%d",&n,&l,&r);
    int u,v;
    for(int i=1;i<n;i++)
        scanf("%d%d",&u,&v),add(u,v),add(v,u);
    dfs(1);Dfs(1);
    for(int i=1;i<=n;i++)
    {
    	int v=up(i,l-1);int u=up(i,r);//从i往上跳 
    	ans=(0ll+ans+(2ll*a[i]+1)*(b[v]-b[u]+mod)+1ll*a[i]*(dep[v]-dep[u]))%mod;
	}
	printf("%d\n",ans);
	return 0;
}

总结

看了个似懂非懂,但是时间非常有限,不能再继续看了。(我连样例答案都推不出来)