题意:有n个节点,n-1条边,1是根节点,每个节点最多两个子节点,其中有2个苹果在点上,他会进入子节点,如果他下面有两个子节点,那么会进入任意一个,问你q次,每次a,b表示两个苹果所在的节点,问最终两个苹果落点有多少种不同可能。
解析:如果点a到底部有x种方案,点b到底部有y种方案,那么总方案数就是x*y。而对于点u,他的方案数取决于子节点的分叉数,因此我们需要自下而上更新父节点的方案贡献值。
注意:题中输入是无向边,但是规定1是根节点,因此需要自己递归求一下每个点的子节点和父节点到底是谁。如果每次都暴力更新到根节点,那么会超时,因此我们还需要记录层数,从下一层一层向上,这样每个点就只会遍历一次。
代码思路:1.将题目给出无向图实现
2.根据无向图和根节点1作出二项图
3.从深度最大子节点,自下而上的更新方案数据
#include <bits/stdc++.h>
using namespace std;
int n,mx,fa[200005];
vector<int> edge[200005],son[200005],levle[200005];
bool st[200005];
long long w[200005];
void dfs(int u,int f,int lev)
{
son[f].push_back(u);//加入子节点
fa[u]=f;//记录父节点
mx=max(mx,lev);//更新最大深度
st[u]=1,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]=0;
w[i]=0;
edge[i].erase(edge[i].begin(),edge[i].end());
son[i].erase(son[i].begin(),son[i].end());
}
}
void solve()
{
cin>>n;
init();//初始化
for(int i=1;i<n;i++)
{
int a,b;cin>>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;cin>>t;
while(t--) solve();
return 0;