【题解 && 树链剖分】 树上相交路径

题目描述:

在这里插入图片描述


Solution

很显然的一个结论:
两条路径相交,当且仅当其中一条路径两个点的 L c a Lca Lca在另一条路径上。

离线询问路径,计算两条路径的 L c a Lca Lca,将 L c a Lca Lca的深度从大到小排序。
对于一组询问 x , y x,y x,y,我们用只要求出 x , y x,y x,y的路径上有多少个 L c a Lca Lca的个数,即当前路径能与多少路径形成相交路径。

考虑树链剖分,用树状数组维护序列 L c a Lca Lca的个数即可。


Code

#include
using namespace std;

#define int long long
const int N = 2e5+10;

int n,m;
struct Node{
	int y,Next;
}e[2*N];
int len = 0 , linkk[N];

struct Tr{
	int tr[N];
	#define lowbit(x) (x&(-x))
	int Ask(int x){int sum = 0; for (int i = x; i; i -= lowbit(i)) sum+=tr[i];return sum;}
	void Change(int x,int v){for (int i = x; i <= n; i+=lowbit(i)) tr[i]+=v;}
}tr;

struct qq{
	int x,y,lca;
}q[N];

int d[N] , fa[N][30];
int num[N] , cnt = 0;
int son[N] , siz[N] , top[N];

void Insert(int x,int y){
	e[++len].Next = linkk[x];
	linkk[x] = len;
	e[len].y = y;
}

void dfs1(int x,int dd,int faa){
	int Max = 0;
	d[x] = dd;
	fa[x][0] = faa;
	siz[x] = 1;
	for (int i = linkk[x]; i; i = e[i].Next){
	    int y = e[i].y;
	    if (y == faa) continue;
		dfs1(e[i].y,dd+1,x);
		siz[x]+=siz[y];
		if (Max < siz[y]) Max = siz[son[x] = y];
	}
}

void dfs2(int x,int id){
	num[x] = ++cnt; top[x] = id;
	if (son[x]) dfs2(son[x] , id);
	for (int i = linkk[x]; i; i = e[i].Next){
		int y = e[i].y;
		if (y == fa[x][0] || y == son[x]) continue;
		dfs2(y,y);
	}
}

void find_fa(){
	for (int i = 1; i < 30; i++)
	  for (int j = 1; j <= n; j++)
	    if (fa[j][i-1] == 0) fa[j][i] = 0;
	    else fa[j][i] = fa[fa[j][i-1]][i-1];
}

int Lca(int x,int y){
	if (d[x] < d[y]) swap(x,y);
	for (int dd = d[x] - d[y] , i = 0; dd; dd>>=1 , i++)
	  if (dd&1) x = fa[x][i];
	if (x == y) return x;
	for (int i = 29; i >= 0; i--)
	  if (fa[x][i] != fa[y][i]) x = fa[x][i] , y = fa[y][i];
	return fa[x][0];
}

bool mycmp(qq x,qq y){
	return d[x.lca] > d[y.lca];
}

int Ask(int x,int y){
	int ans = 0;
	while (top[x] != top[y]){
		if (d[top[x]] < d[top[y]]) swap(x,y);
		ans+=tr.Ask(num[x]) - tr.Ask(num[top[x]] - 1);
		x = fa[top[x]][0];
	}
	if (d[x] < d[y]) swap(x,y);
	ans+=tr.Ask(num[x]) - tr.Ask(num[y]-1);
	return ans;
}

signed main(){
	freopen("tree.in","r",stdin);
	freopen("tree.out","w",stdout);
	scanf("%lld %lld",&n,&m);
	for (int i = 1,x,y; i < n; i++)
	  scanf("%lld %lld",&x,&y) , Insert(x,y) , Insert(y,x);
	dfs1(1,0,0);
	dfs2(1,0);
	find_fa();
	for (int i = 1; i <= m; i++)
	  scanf("%lld %lld",&q[i].x,&q[i].y) , q[i].lca = Lca(q[i].x,q[i].y);
	sort(q+1,q+m+1,mycmp);
	int ans = 0;
	for (int i = 1; i <= m; i++){
		ans+=Ask(q[i].x,q[i].y);
		tr.Change(num[q[i].lca],1);
	}
	printf("%lld",ans);
	return 0;
}

你可能感兴趣的:(数据结构,题解,树链剖分)