传送门
解析:
首先怎么判断树上两个路径相交,其中一条路径的LCALCALCA必然在另外一条路径上。
所以问题变成了,新增一条路径,询问有多少其他路径的LCALCALCA在它上面,询问它的LCALCALCA在多少其他路径上面。注意处理LCALCALCA重合的情况。
这个显然的做法就是维护两个线段树,然后树链剖分维护路径加,单点求值和单点加,路径求值,注意“和”字分开的两个部分不能混为一谈。
但是树链剖分显然是O(nlog2n)O(n\log^2n)O(nlog2n)的,复杂度不够优秀,只能用来对拍。代码我放在最下面了。
这道题可以做到O(nlogn)O(n\log n)O(nlogn)。
考虑用差分的思想以及DFSDFSDFS序和BITBITBIT来完成这道题。其中路径操作通过差分转换为点到根的路径操作,称之为链。
对于单点加,链求和,我们考虑什么样的单点加才对链有贡献,当且仅当这条链的末端在以这个点为根的子树当中,那么这个单点加对链有贡献的链的末端都在它的子树中。
所以单点加转换为子树加,链求和就是在链的末端单点求和。
同理,链加就是在链的末端加,单点求和就是子树求和就行了。
代码(DFS序+树状数组):
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
inline int getint(){
re int num;
re char c;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
return num;
}
cs int N=1000006;
int last[N],nxt[N<<1],to[N<<1],ecnt;
inline void addedge(int u,int v){
nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v;
nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u;
}
int n,m;
//Tree_Dissection
int fa[N],son[N],siz[N],dep[N],top[N],in[N],out[N],tot;
inline void dfs1(int u){
siz[u]=1;
for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
if(v==fa[u])continue;
fa[v]=u;
dep[v]=dep[u]+1;
dfs1(v);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
}
inline void dfs2(int u){
in[u]=++tot;
for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
if(v==fa[u])continue;
if(v==son[u])top[v]=top[u];
else top[v]=v;
dfs2(v);
}
out[u]=tot;
}
inline void tree_dissection(int root=1){
dfs1(root);
top[root]=root;
dfs2(root);
}
inline int LCA(int u,int v){
while(top[u]!=top[v]){
if(dep[top[u]]>dep[top[v]])swap(u,v);
v=fa[top[v]];
}
return dep[u]>dep[v]?v:u;
}
//BIT
#define lowbit(x) (x&(-x))
int t1[N],t2[N];
inline void add(int *cs tr,int pos,int val){
for(++pos;pos;pos-=lowbit(pos))tr[pos]+=val;
}
inline int query(int *cs tr,int pos,int res=0){
for(++pos;pos<N;pos+=lowbit(pos))res+=tr[pos];
return res;
}
ll ans;
int cnt[N];
inline void query(int u,int v){
int lca=LCA(u,v);
ans+=query(t1,in[u])+query(t1,in[v])-(query(t1,in[lca])<<1);
ans+=query(t2,in[lca])-query(t2,out[lca]+1);
ans+=cnt[lca];
}
inline void update(int u,int v){
int lca=LCA(u,v);
add(t1,in[lca]-1,-1);add(t1,out[lca],1);
add(t2,in[u],1);add(t2,in[v],1);add(t2,in[lca],-2);
++cnt[lca];
}
signed main(){
n=getint();
m=getint();
for(int re i=1;i<n;++i){
int u=getint(),v=getint();
addedge(u,v);
}
tree_dissection();
while(m--){
int u=getint(),v=getint();
query(u,v);
update(u,v);
}
cout<<ans;
cerr<<"ok\n";
return 0;
}
代码(考场上对拍还慢的一的树链剖分):
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const
#define int ll
inline int getint(){
re int num;
re char c;
while(!isdigit(c=gc()));num=c^48;
while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48);
return num;
}
cs int N=1000006;
int last[N],nxt[N<<1],to[N<<1],ecnt;
inline void addedge(int u,int v){
nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v;
nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u;
}
int fa[N],dep[N],son[N],siz[N],top[N],in[N],pos[N],tot;
inline void dfs1(int u){
siz[u]=1;
for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
if(v==fa[u])continue;
fa[v]=u;
dep[v]=dep[u]+1;
dfs1(v);
siz[u]+=siz[v];
if(siz[v]>siz[son[u]])son[u]=v;
}
}
inline void dfs2(int u){
pos[in[u]=++tot]=u;
if(son[u]){
top[son[u]]=top[u];
dfs2(son[u]);
}
for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){
if(v==son[u]||v==fa[u])continue;
top[v]=v;
dfs2(v);
}
}
inline void tree_dissection(int root=1){
dfs1(root);
top[root]=root;
dfs2(root);
}
inline int LCA(int u,int v){
while(top[u]!=top[v]){
if(dep[top[u]]>dep[top[v]])swap(u,v);
v=fa[top[v]];
}
return dep[u]>dep[v]?v:u;
}
int sum1[N<<2],sum2[N<<2];
inline void update1(int k,int l,int r,cs int &pos,cs int &val){
sum1[k]+=val;
if(l==r)return ;
int mid=(l+r)>>1;
if(pos<=mid)update1(k<<1,l,mid,pos,val);
else update1(k<<1|1,mid+1,r,pos,val);
}
inline int query1(int k,int l,int r,cs int &ql,cs int &qr){
if(ql<=l&&r<=qr)return sum1[k];
int mid=(l+r)>>1;
if(qr<=mid)return query1(k<<1,l,mid,ql,qr);
if(ql>mid)return query1(k<<1|1,mid+1,r,ql,qr);
return query1(k<<1,l,mid,ql,qr)+query1(k<<1|1,mid+1,r,ql,qr);
}
inline void update2(int k,int l,int r,cs int &ql,cs int &qr,cs int &val){
if(ql<=l&&r<=qr)return (void)(sum2[k]+=val);
int mid=(l+r)>>1;
if(ql<=mid)update2(k<<1,l,mid,ql,qr,val);
if(qr>mid)update2(k<<1|1,mid+1,r,ql,qr,val);
}
inline int query2(int k,int l,int r,cs int &pos){
if(l==r)return sum2[k];
int mid=(l+r)>>1;
if(pos<=mid)return query2(k<<1,l,mid,pos)+sum2[k];
else return query2(k<<1|1,mid+1,r,pos)+sum2[k];
}
ll ans;
int cnt[N];
int n,m;
inline int querypath(int u,int v,int res=0){
while(top[u]!=top[v]){
if(dep[top[u]]>dep[top[v]])swap(u,v);
res+=query1(1,1,n,in[top[v]],in[v]);
v=fa[top[v]];
}
if(dep[u]>dep[v])swap(u,v);
res+=query1(1,1,n,in[u],in[v]);
return res;
}
inline void query(int u,int v){
int lca=LCA(u,v);
ans+=query2(1,1,n,in[lca]);
ans+=querypath(u,v);
ans-=cnt[lca];
}
inline void modifypath(int u,int v){
while(top[u]!=top[v]){
if(dep[top[u]]>dep[top[v]])swap(u,v);
update2(1,1,n,in[top[v]],in[v],1);
v=fa[top[v]];
}
if(dep[u]>dep[v])swap(u,v);
update2(1,1,n,in[u],in[v],1);
}
inline void update(int u,int v){
int lca=LCA(u,v);
update1(1,1,n,in[lca],1);
modifypath(u,v);
++cnt[lca];
}
signed main(){
n=getint();
m=getint();
for(int re i=1;i<n;++i){
int u=getint(),v=getint();
addedge(u,v);
}
tree_dissection();
while(m--){
int u=getint(),v=getint();
query(u,v);
update(u,v);
}
cout<<ans;
return 0;
}