[杂题 树状数组] 51Nod1681 公共祖先

不错的题,思路很套路。考虑求的答案是

i,jk[ki,j]

改变枚举顺序,设 Son1(x) 表示第一棵树中以x为根的子树的点集,答案就变成:
k(|Son1(k)Son2(k)|2)

这个东西直接dfs序+树状数组。对一棵数建 dfs 序,另一棵 dfs 一趟,统计答案。
不需要主席树,每个节点只进行一次询问,可以算进入这个子树的之前和之后的差值,就是子树的答案。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=100005,maxe=100005;
int n,dfn[maxn],out[maxn],dge[2][maxn],rt0,rt1;
int fir[2][maxn],nxt[2][maxe],son[2][maxe],tot[2];
LL ans;
void add(int k,int x,int y){
    son[k][++tot[k]]=y; nxt[k][tot[k]]=fir[k][x]; fir[k][x]=tot[k];
}
int Tim;
void dfs0(int x){
    dfn[x]=++Tim;
    for(int j=fir[0][x];j;j=nxt[0][j]) dfs0(son[0][j]);
    out[x]=Tim;
}
int bit[maxn];
void Update(int x,int val){
    for(;x<=n;x+=(x&(-x))) bit[x]+=val;
}
int Query(int x){
    int res=0;
    for(;x;x-=(x&(-x))) res+=bit[x];
    return res;
}
void dfs1(int x){
    LL _t=Query(out[x])-Query(dfn[x]-1);
    for(int j=fir[1][x];j;j=nxt[1][j]) dfs1(son[1][j]);
    LL t=Query(out[x])-Query(dfn[x]-1)-_t; //printf("%d : %d\n",x,t); 
    ans+=t*(t-1)/2;
    Update(dfn[x],1);
}
int main(){
    freopen("51nod1681.in","r",stdin);
    freopen("51nod1681.out","w",stdout);
    scanf("%d",&n);
    for(int i=1;i<=n-1;i++){
        int x,y; scanf("%d%d",&x,&y);
        add(0,x,y); dge[0][y]++;
    }
    for(int i=1;i<=n-1;i++){
        int x,y; scanf("%d%d",&x,&y);
        add(1,x,y); dge[1][y]++;
    }
    for(int i=1;i<=n;i++) if(!dge[0][i]) rt0=i;
    for(int i=1;i<=n;i++) if(!dge[1][i]) rt1=i;
    dfs0(rt0); dfs1(rt1);
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值