[51nod 1681] common ancestor (DFS segment tree combined sequence +)

[51nod 1681] common ancestor (DFS segment tree combined sequence +)

Face questions

Given two n (n <= 100000) points trees, for all points on the evaluation of the number of their common ancestor common to the two trees and the.

As shown, for the point (2,4), their common ancestor is the first tree in the {1,3,5}, in the second tree in the common ancestor is {1}, so public common ancestor number 2

All this points to add up the number, get the final answer

analysis

\ (O (n ^ 3) \) violence do not speak up, consider \ (O (n ^ 2) \) approach

Enumeration point of complexity is too high, is not feasible. We consider the number of times each node x as a public common ancestor. Provided on the tree node x A, B corresponding to the node in the tree is x '(in fact, x' and x are numbered the same, so that only the convenience of description). If the point of the subtree of x both in correspondence the common ancestor common to B and then on the x 'sub-tree, then this point would contain x. Note that a small detail, if x is father of y, x do not ancestors of x and y, so here the "sub-tree" should not include the X- .

As this figure, A subtree of node has a {2,3,4,5}, {2,3,4,5} are mapped to the B 1 in the subtree. Optionally one pair of the four nodes, they contain a common ancestor

So long as we consider the subtree of x how many points correspond to the last x 'in the sub-tree B tree can be. Each node x of violence enumeration subtree, then judgment. This point number is set to cnt, then x is a number that is common to a common ancestor \ (C_ {CNT} ^ 2 \) , it accumulates into the answer

那么我们怎么把它优化呢?我们发现,节点编号是离散的,不好判断。但子树中节点的dfs序是连续的。我们把A中节点x的dfs序标记到树B上对应的位置x‘。然后我们遍历树A的每个节点x,它子树的dfs序范围为[l[x]+1,r[x]] (不包含x)。那么问题就变成在树B上编号为x的节点的子树中有多少个节点的标记落在[l[x]+1,r[x]]的范围内

如图,我们想求A中3的子树中有多少个节点对应到B中也在3的子树里,l[3]=2,r[3]=5,B中3的子树中的dfs序有{2,4},落在[2+1,5]的范围内的只有4,所以有1个节点

这是线段树合并的经典问题。用权值线段树合并就可以了,节点x的线段树的节点[l,r] 存储有x的子树中多少个值落在[l,r]内。(有些题解用了可持久化线段树,其实没有必要)。我们遍历的时候从下往上合并,合并到节点x的时候就更新x的cnt值。

时间复杂度\(O(n\log n)\)

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define maxn 100000
#define maxlogn 25 
using namespace std;
int n;
struct segment_tree{
#define lson(x) (tree[x].ls)
#define rson(x) (tree[x].rs) 
    struct node{
        int ls;
        int rs;
        int val;
    }tree[maxn*maxlogn+5];
    int ptr;
    void push_up(int x){
        tree[x].val=tree[lson(x)].val+tree[rson(x)].val;
    } 
    void update(int &x,int upos,int l,int r){
        if(!x) x=++ptr;
        if(l==r){
            tree[x].val++;
            return;
        }
        int mid=(l+r)>>1;
        if(upos<=mid) update(tree[x].ls,upos,l,mid);
        else update(tree[x].rs,upos,mid+1,r);
        push_up(x); 
    }
    int query(int x,int L,int R,int l,int r){
        if(L<=l&&R>=r){
            return tree[x].val;
        }
        int mid=(l+r)>>1;
        int ans=0;
        if(L<=mid) ans+=query(tree[x].ls,L,R,l,mid);
        if(R>mid) ans+=query(tree[x].rs,L,R,mid+1,r);
        return ans;
    }
    int merge(int x,int y,int l,int r){
        if(!x||!y) return x+y;
        if(l==r){
            tree[x].val+=tree[y].val;
            return x;
        }
        int mid=(l+r)>>1;
        tree[x].ls=merge(tree[x].ls,tree[y].ls,l,mid);
        tree[x].rs=merge(tree[x].rs,tree[y].rs,mid+1,r);
        push_up(x);
        return x;
    }
}T;
int root[maxn+5];
int in[maxn+5];

int tim=0;
int dfnl[maxn+5],dfnr[maxn+5];
vector<int>E1[maxn+5],E2[maxn+5];
void dfs1(int x,int fa){
    dfnl[x]=++tim;
    for(int i=0;i<E1[x].size();i++){
        int y=E1[x][i];
        if(y!=fa){
            dfs1(y,x);
        }
    } 
    dfnr[x]=tim;
} 

int cnt[maxn+5];
void dfs2(int x,int fa){
    for(int i=0;i<E2[x].size();i++){
        int y=E2[x][i];
        if(y!=fa){
            dfs2(y,x);
            root[x]=T.merge(root[x],root[y],1,n);
        }
    }
    cnt[x]=T.query(root[x],dfnl[x]+1,dfnr[x],1,n);
}

int main(){
    int u,v;
    int rt1,rt2;
    scanf("%d",&n);
    for(int i=1;i<n;i++){
        scanf("%d %d",&u,&v);
        E1[u].push_back(v);
        E1[v].push_back(u);
        in[v]++;
    }
    for(int i=1;i<=n;i++) if(in[i]==0) rt1=i;//根不一定是1 
    memset(in,0,sizeof(in));
    
    for(int i=1;i<n;i++){
        scanf("%d %d",&u,&v);
        E2[u].push_back(v);
        E2[v].push_back(u);
        in[v]++;
    }
    for(int i=1;i<=n;i++) if(in[i]==0) rt2=i;
    
    dfs1(rt1,0);
    for(int i=1;i<=n;i++){
        T.update(root[i],dfnl[i],1,n);
    }
    dfs2(rt2,0);
    long long ans=0;
    for(int i=1;i<=n;i++){
        ans+=(long long)cnt[i]*(cnt[i]-1)/2;
    }
    printf("%lld\n",ans);
}

Guess you like

Origin www.cnblogs.com/birchtree/p/11228847.html