https://www.luogu.org/problemnew/show/P5327c++
首先考慮每一個點可以到達的集合,它在樹上必定是一個聯通塊。git
先考慮枚舉每一個點,而後算它能到達多少點。spa
而後咱們把全部跨越這個點的鏈全都拿出來,那麼這個聯通塊的邊數=這個點可以訪問的點數=這些鏈的鏈並。code
考慮傳統的求鏈並的方法,就是按照$dfs$序排序,而後答案就是每一個點的deep減去相鄰兩個點的$lca$的$deep$再減去全部點的$lca$的$deep$。排序
這就是大體思路,再考慮如何維護答案。get
用相似樹上差分的思想,對於每一條鏈,在$u$和$v$處分別打上$u:+1$和$v:+1$的標記,再在lca和$fa[lca]$處打上$-1$的標記。it
而後用線段樹合併維護全部標記的貢獻便可。class
#include<bits/stdc++.h> #define N 100009 #define inf 2e9 #define ls tr[cnt].l #define rs tr[cnt].r using namespace std; typedef long long ll; int lo[N<<1],head[N],tot,tott,deep[N],mp[N],dfn[N],p[20][N<<1],now,fa[N],T[N],n,m,_tag[N]; ll ans; vector<int>vec[N]; inline ll rd(){ ll x=0;char c=getchar();bool f=0; while(!isdigit(c)){if(c=='-')f=1;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} return f?-x:x; } struct edge{int n,to;}e[N<<1]; struct seg{ int l,r,mi,ma,cnt; ll val; }tr[N*80]; inline void add(int u,int v){e[++tot].n=head[u];e[tot].to=v;head[u]=tot;} inline int maxx(int u,int v){return deep[u]>deep[v]?v:u;} inline int _min(int u,int v){if(!u||!v)return u|v;return dfn[u]<dfn[v]?u:v;} inline int _max(int u,int v){if(!u||!v)return u|v;return dfn[u]>dfn[v]?u:v;} inline int getlca(int u,int v){ if(!u||!v)return 0; u=mp[u];v=mp[v]; if(u>v)swap(u,v); int loo=lo[v-u+1]; return maxx(p[loo][u],p[loo][v-(1<<loo)+1]); } inline void pushup(int cnt){ tr[cnt].val=tr[ls].val+tr[rs].val-deep[getlca(tr[ls].ma,tr[rs].mi)]; tr[cnt].ma=_max(tr[ls].ma,tr[rs].ma); tr[cnt].mi=_min(tr[ls].mi,tr[rs].mi); } void dfs(int u){ dfn[u]=++dfn[0];_tag[dfn[0]]=u; p[0][++now]=u;mp[u]=now; for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa[u]){ int v=e[i].to;deep[v]=deep[u]+1;fa[v]=u; dfs(v);p[0][++now]=u; } } void upd(int &cnt,int l,int r,int x,int tag){ if(!cnt)cnt=++tott; if(l==r){ tr[cnt].cnt+=tag; if(tr[cnt].cnt) tr[cnt].mi=tr[cnt].ma=x,tr[cnt].val=deep[x]; else tr[cnt].mi=tr[cnt].ma=tr[cnt].val=0; return; } int mid=(l+r)>>1; if(dfn[x]<=mid)upd(ls,l,mid,x,tag); else upd(rs,mid+1,r,x,tag); pushup(cnt); } int merge(int u,int v,int l,int r){ if(!u||!v)return u^v; if(l==r){ tr[u].cnt+=tr[v].cnt; tr[u].ma=_max(tr[u].ma,tr[v].ma); tr[u].mi=_min(tr[u].mi,tr[v].mi); tr[u].val=tr[u].cnt?deep[_tag[l]]:0; return u; } int mid=(l+r)>>1; tr[u].l=merge(tr[u].l,tr[v].l,l,mid); tr[u].r=merge(tr[u].r,tr[v].r,mid+1,r); pushup(u); return u; } void work(int u){ for(int i=head[u];i;i=e[i].n)if(e[i].to!=fa[u]){ int v=e[i].to; work(v);T[u]=merge(T[u],T[v],1,n); } for(vector<int>::iterator it=vec[u].begin();it!=vec[u].end();++it){ int v=*it; upd(T[u],1,n,v,-1); } // cout<<tr[T[u]].val<<" "; int x=tr[T[u]].val-deep[getlca(tr[T[u]].mi,tr[T[u]].ma)]; ans+=x; } int main(){ n=rd();m=rd(); int u,v; for(int i=1;i<n;++i){ u=rd();v=rd(); add(u,v);add(v,u); } dfs(1); for(int i=2;i<=now;++i)lo[i]=lo[i>>1]+1; for(int i=1;(1<<i)<=now;++i) for(int j=1;j+(1<<i)-1<=now;++j)p[i][j]=maxx(p[i-1][j],p[i-1][j+(1<<i-1)]); for(int i=1;i<=m;++i){ u=rd();v=rd(); int lca=getlca(u,v); vec[lca].push_back(u); vec[lca].push_back(v); vec[fa[lca]].push_back(u); vec[fa[lca]].push_back(v); upd(T[u],1,n,u,1);upd(T[u],1,n,v,1); upd(T[v],1,n,u,1);upd(T[v],1,n,v,1); } work(1); printf("%lld\n",ans/2); return 0; }