傳送門
解析:
首先怎麼判斷樹上兩個路徑相交,其中一條路徑的必然在另外一條路徑上。
所以問題變成了,新增一條路徑,詢問有多少其他路徑的在它上面,詢問它的在多少其他路徑上面。注意處理重合的情況。
這個顯然的做法就是維護兩個線段樹,然後樹鏈剖分維護路徑加,單點求值和單點加,路徑求值,注意“和”字分開的兩個部分不能混爲一談。
但是樹鏈剖分顯然是的,複雜度不夠優秀,只能用來對拍。代碼我放在最下面了。
這道題可以做到。
考慮用差分的思想以及序和來完成這道題。其中路徑操作通過差分轉換爲點到根的路徑操作,稱之爲鏈。
對於單點加,鏈求和,我們考慮什麼樣的單點加纔對鏈有貢獻,當且僅當這條鏈的末端在以這個點爲根的子樹當中,那麼這個單點加對鏈有貢獻的鏈的末端都在它的子樹中。
所以單點加轉換爲子樹加,鏈求和就是在鏈的末端單點求和。
同理,鏈加就是在鏈的末端加,單點求和就是子樹求和就行了。
代碼(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;
}