2018.11.05【校內模擬】相交(DFS序)(樹狀數組)(用來對拍的樹鏈剖分)

傳送門


解析:

首先怎麼判斷樹上兩個路徑相交,其中一條路徑的LCALCA必然在另外一條路徑上。

所以問題變成了,新增一條路徑,詢問有多少其他路徑的LCALCA在它上面,詢問它的LCALCA在多少其他路徑上面。注意處理LCALCA重合的情況。

這個顯然的做法就是維護兩個線段樹,然後樹鏈剖分維護路徑加,單點求值和單點加,路徑求值,注意“和”字分開的兩個部分不能混爲一談。

但是樹鏈剖分顯然是O(nlog2n)O(n\log^2n)的,複雜度不夠優秀,只能用來對拍。代碼我放在最下面了。

這道題可以做到O(nlogn)O(n\log n)

考慮用差分的思想以及DFSDFS序和BITBIT來完成這道題。其中路徑操作通過差分轉換爲點到根的路徑操作,稱之爲鏈。

對於單點加,鏈求和,我們考慮什麼樣的單點加纔對鏈有貢獻,當且僅當這條鏈的末端在以這個點爲根的子樹當中,那麼這個單點加對鏈有貢獻的鏈的末端都在它的子樹中。

所以單點加轉換爲子樹加,鏈求和就是在鏈的末端單點求和。

同理,鏈加就是在鏈的末端加,單點求和就是子樹求和就行了。


代碼(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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章