CF Round 881--D. Apple Tree

Timofey has an apple tree growing in his garden; it is a rooted tree of n vertices with the root in vertex 1 (the vertices are numbered from 1 to n). A tree is a connected graph without loops and multiple edges.

This tree is very unusual — it grows with its root upwards. However, it's quite normal for programmer's trees.

The apple tree is quite young, so only two apples will grow on it. Apples will grow in certain vertices (these vertices may be the same). After the apples grow, Timofey starts shaking the apple tree until the apples fall. Each time Timofey shakes the apple tree, the following happens to each of the apples:

Let the apple now be at vertex u.

  • If a vertex u has a child, the apple moves to it (if there are several such vertices, the apple can move to any of them).
  • Otherwise, the apple falls from the tree.

It can be shown that after a finite time, both apples will fall from the tree.

Timofey has q assumptions in which vertices apples can grow. He assumes that apples can grow in vertices x and y, and wants to know the number of pairs of vertices (a, b) from which apples can fall from the tree, where a — the vertex from which an apple from vertex x will fall, b — the vertex from which an apple from vertex y will fall. Help him do this.

Input

The first line contains integer t (1≤t≤10^4) — the number of test cases.

The first line of each test case contains integer n (2≤n≤2⋅10^5) — the number of vertices in the tree.

Then there are n−1 lines describing the tree. In line i there are two integers ui and vi (1≤ui,vi≤n, ui≠vi) — edge in tree.

The next line contains a single integer q (1≤q≤2⋅10^5) — the number of Timofey's assumptions.

Each of the next q lines contains two integers xi and yi (1≤xi,yi≤n) — the supposed vertices on which the apples will grow for the assumption i.

It is guaranteed that the sum of n does not exceed 2⋅10^5. Similarly, It is guaranteed that the sum of q does not exceed 2⋅10^5.

Output

For each Timofey's assumption output the number of ordered pairs of vertices from which apples can fall from the tree if the assumption is true on a separate line.

题意:有n个节点,n-1条边,1是根节点,每个节点最多两个子节点,其中有2个苹果在点上,他会进入子节点,如果他下面有两个子节点,那么会进入任意一个,问你q次,每次a,b表示两个苹果所在的节点,问最终两个苹果落点有多少种不同可能。

解析:如果点a到底部有x种方案,点b到底部有y种方案,那么总方案数就是x*y。而对于点u,他的方案数取决于子节点的分叉数,因此我们需要自下而上更新父节点的方案贡献值。

注意:题中输入是无向边,但是规定1是根节点,因此需要自己递归求一下每个点的子节点和父节点到底是谁。如果每次都暴力更新到根节点,那么会超时,因此我们还需要记录层数,从下一层一层向上,这样每个点就只会遍历一次。

#include <bits/stdc++.h>
using namespace std;
const int N=2e5+5;
typedef long long ll;
int n,mx,fa[N];//mx表示最大达到层数,fa表示该点的父节点是谁
vector<int> edge[N],son[N],levle[N];
bool st[N];//判断已经遍历过
ll w[N];//表示该点到底部有几种选择
void dfs(int u,int f,int lev)//当前点u,父亲f,层数lev
{
    son[f].push_back(u);//加入子节点
    fa[u]=f;//记录父节点
    mx=max(mx,lev);//更新最大深度
    st[u]=true,levle[lev].push_back(u);//该层存入该点
    for(int i=0;i<edge[u].size();i++)
    {
        int j=edge[u][i];
        if(!st[j]) dfs(j,u,lev+1);//防止回搜
    }
}
void init()
{
    for(int i=0;i<=mx;i++) levle[i].erase(levle[i].begin(),levle[i].end());
    mx=0;
    for(int i=0;i<=n;i++)
    {
        st[i]=false;
        w[i]=0;
        edge[i].erase(edge[i].begin(),edge[i].end());
        son[i].erase(son[i].begin(),son[i].end());
    }
}
void solve()
{
    scanf("%d",&n);
    init();
    for(int i=1;i<n;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        edge[a].push_back(b);
        edge[b].push_back(a);
    }
    dfs(1,0,1);
    for(int i=1;i<=n;i++) if(!son[i].size()) w[i]=1;//没儿子就是最底层
    for(int i=mx;i>=2;i--)//自下往上更新w
    {
        for(int j=0;j<levle[i].size();j++)
        {
            int u=levle[i][j];
            int f=fa[u];//父节点
            w[f]+=w[u];
        }
    }
    int q;
    scanf("%d",&q);
    while(q--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        printf("%lld\n",w[a]*w[b]);
    }
}
int main()
{
    int t=1;
    scanf("%d",&t);
    while(t--) solve();
    return 0;
}
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值