【jzoj5290】【NOIP2017提高組A組模擬8.17】【行程的交集】

Description

豪哥生活在一個n個點的樹形城市裏面,每一天都要走來走去。雖然走的是比較的多,但是豪哥在這個城市裏面的朋友並不是很多。

當某一天,猴哥給他展現了一下大佬風範之後,豪哥決定要獲得一些交往機會來提升交往能力。豪哥現在已經物色上了一條友,打算和它(豪哥並不讓喫瓜羣衆知道性別)交往。豪哥現在spy了一下這個人的所有行程起點和終點,豪哥打算從終點開始走到起點與其相遇。但是豪哥是想找話題的,他想知道以前有多少次行程和此次行程是有交集的,這樣豪哥就可以搭上話了。這個路徑與之前路徑的有交集數量作爲豪哥此次的交往機會。

但是豪哥急着要做交往準備,所以算什麼交往機會的小事情就交給你了。

Solution

這題其實很妙,兩條路徑有交有兩種情況,lca在當前路徑上,lca在根到當前lca路徑上。第一種情況建一個樹狀數組,加入路徑時lca dfn打+1,leave+1打-1,貢獻爲1~u+1~v-2*1~lca。第二種情況另建一個樹狀樹狀數組,加入路徑u,v打+1,lca打-2,貢獻爲lca dfn~leave。

code

#include<set>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long
#define fo(i,j,k) for(int i=j;i<=k;i++)
#define fd(i,j,k) for(int i=j;i>=k;i--)
#define fr(i,j) for(int i=begin[j];i;i=next[i])
using namespace std;
int const mn=2*1e5+9,mm=4*1e5+9,mo=1e9+7;
int n,m,gra,lg2,tim,dep[mn],fa[mn][19],begin[mn],to[mm],next[mm],dfn[mn],
    end[mn],tr[mn][2],cnt[mn];
void insert(int u,int v){
    to[++gra]=v;
    next[gra]=begin[u];
    begin[u]=gra;
}
void dfs(int p,int q){
    dfn[p]=++tim;
    fa[p][0]=q;
    dep[p]=dep[q]+1;
    fr(i,p)if(to[i]!=q)dfs(to[i],p);
    end[p]=tim;
}
int lc(int u,int v){
    if(dep[u]<dep[v])swap(u,v);
    fd(i,lg2,0)if(dep[fa[u][i]]>=dep[v])u=fa[u][i];
    if(u==v)return u;
    fd(i,lg2,0)if(fa[u][i]!=fa[v][i])u=fa[u][i],v=fa[v][i];
    return fa[u][0];
}
void oper(int p,int op,int v){
    while(p<=n){
        tr[p][op]+=v;
        p+=p&(-p);
    }
}
int qury(int p,int op){
    int ans=0;
    while(p>0){
        ans+=tr[p][op];
        p-=p&(-p);
    }
    return ans;
}
int main(){
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
    scanf("%d",&n);
    fo(i,1,n-1){
        int u,v;
        scanf("%d%d",&u,&v);
        insert(u,v);
        insert(v,u);
    }
    dfs(1,0);
    lg2=log(n)/log(2);
    fo(j,1,lg2)fo(i,1,n)fa[i][j]=fa[fa[i][j-1]][j-1];
    scanf("%d",&m);
    fo(cas,1,m){
        int u,v;
        scanf("%d%d",&u,&v);
        int lca=lc(u,v);
        printf("%d\n",qury(dfn[u],1)+qury(dfn[v],1)-2*qury(dfn[lca],1)+
            qury(end[lca],2)-qury(dfn[lca]-1,2)+cnt[lca]);
        cnt[lca]++;
        oper(dfn[lca],1,1);
        oper(end[lca]+1,1,-1);
        oper(dfn[lca],2,-2);
        oper(dfn[u],2,1);
        oper(dfn[v],2,1);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章