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
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;
}