[11.02] 猴猴的比赛

题目描述

猴猴今天要和小伙伴猩猩比赛爬树,为了公平不碰撞,猴猴和猩猩需要在不同的树上攀爬。于是它们选了两颗节点数同为n的树,并将两棵树的节点分别以1~n标号(根节点标号为1),但两棵树的节点连接方式不尽相同。

现在它们决定选择两个标号的点进行比赛。为了方便统计,规定它们比赛中必须都向上爬。(即选定的赛段节点u→节点v都必须指向叶子方向)请你求出这两棵树上共有多少对节点满足比赛的需求。

题目解析

先求出第一棵树的 d f s dfs dfs序,得到每个节点本身的编号以及子树的编号范围 [ l i , r i ] [l_i,r_i] [li,ri] 。 用树状数组来维护后面的计算。 在 D F S DFS DFS处理第二棵树时,我们每处理完一个节点,就将该节点在第一棵树中的对应编号设为 1 1 1,处理完所有子节点后,查询编号区间 [ l i , r i ] [l_i,r_i] [li,ri]的区间和,即当前子树下,有多少个节点在树 1 1 1中也是当前节点的子孙节点。维护计数即可。

代码

#include<cstdio>
#include<cstring>
using namespace std;
const int N=100000+5;

int n,cnt,ls[N],next[2*N],v[2*N];
inline void add(int x,int y) 
{
	next[++cnt]=ls[x]; ls[x]=cnt;v[cnt]=y; 
  	next[++cnt]=ls[y]; ls[y]=cnt;v[cnt]=x; 
}
int id,dfn[N],siz[N];
void pre(int x,int fa)
{
	dfn[x]=++id; siz[x]=1;
  	for(int i=ls[x];i;i=next[i])
   	{
	  int y=v[i];
      if(y==fa) continue; 
      pre(y,x);
	  siz[x]+=siz[y];
   	}
}
int t[N],ans[N];
void change(int x,int num) {for(;x<=n;x+=x&-x) t[x]+=num;}
int ask(int x) {int ret=0;for(;x;x-=x&-x) ret+=t[x];return ret;} 
void dfs(int x,int fa)
{
	int cmp=0;
  	for(int i=ls[x];i;i=next[i])
   	{
	  int y=v[i];
      if(y==fa) continue;
      cmp=ask(dfn[y]+siz[y]-1)-ask(dfn[y]-1); 
      dfs(y,x); 
      ans[y]-=cmp; 
      change(dfn[y],1); 
   	}
	ans[x]=ask(dfn[x]+siz[x]-1)-ask(dfn[x]-1); 
} 
int main()
{    
 	int x,y; scanf("%d",&n);    
 	for(int i=1;i<n;i++) scanf("%d%d",&x,&y),add(x,y);
 	pre(1,0);    
 	cnt=0;memset(ls,0,sizeof(ls));     
 	for(int i=1;i<n;i++) scanf("%d%d",&x,&y),add(x,y); 
 	dfs(1,0);    
 	for(int i=1;i<=n;i++) ans[0]+=ans[i];
 	printf("%d",ans[0]);
	return 0;     
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值