CF1213G Path Queries

CF1213G Path Queries

Problem

给一棵\(n\)点边带权的树,有\(m\)个询问,每次询问树上有多少最大权值不大于\(q\)的简单路径。单独一个点不算路径。

\(1≤n,m≤2\times10^5\)

\(1\le u,v\le n\)

\(1\le w,q \le 2\times 10^5\)

Solution

第一眼是一个裸的点分治。

将所有询问离线并排序。对于每一个询问\(q\),都跑一遍点分治,这样的时间复杂度是\(O(nm\log n)\),完全过不去。

发现权值\(w,q\le 2\times 10^5\),这里是一个重要的突破口。

不如只跑一遍点分治但是用一个桶来统计最大权值等于\(x\)的路径的数量

最后求一个前缀和即可,注意排除路径长度为\(0\)的情况即可。

时间复杂度\(O(n\log n)\),空间复杂度\(O(n)\)


但是这里给出一种更简单但是思维难度更高(?)的算法。

先将所有操作离线。类比Kruskal算法,将所有的边按边权排序并用并查集维护一个连通块大小。假如有一个连通块的大小是\(size\),那么这个连通块中就有\(\dfrac {size\times(size-1)}{2}\)条简单路径。

那么我们按照排好序的询问从小到大加边,加完所有边权小于当前询问的\(q\)的边。此时,

\[ans=\sum \dfrac {size\times(size-1)}{2} \]

使用并查集动态维护这个\(ans\)即可。

这是离线做法。仿照上面的点分治,由于权值范围小的可怜,我们只需要把所有的点权都当作一个询问,就变成所谓的在线做法了。

注意答案是\(n^2\)级别的,需要开\(\text{long long}\)

Code

离线

/**************************************************************
 * Problem: CF1213G Path Queries-bcj-no-online
 * Author: Vanilla_chan
 * Date: 20210309
**************************************************************/
//预编译部分已略去
#define N 200010
struct Ask
{
	int id,q;
	bool operator<(const Ask& z)const
	{
		return q<z.q;
	}
}ask[N];
int f[N],sze[N];
LL S;
int getf(int x)
{
	if(f[x]==x) return x;
	return f[x]=getf(f[x]);
}
LL calc(LL x)
{
	return x*(x-1)/2;
}
void merge(int x,int y)
{
	x=getf(x);
	y=getf(y);
	if(x==y) return;
	S-=calc(sze[x])+calc(sze[y]);
	if(sze[x]<sze[y])
	{
		f[x]=y;
		sze[y]+=sze[x];
		sze[x]=0;
	}
	else
	{
		f[y]=x;
		sze[x]+=sze[y];
		sze[y]=0;
	}
	S+=calc(sze[f[x]]);
	
}
int n,m;
struct edge
{
	int x,y,z;
	bool operator<(const edge& b)const
	{
		return z<b.z;
	}
}e[N];
LL ans[N];
int main()
{
	n=read();
	m=read();
	for(int i=1;i<n;i++)
	{
		e[i].x=read();
		e[i].y=read();
		e[i].z=read();
	}
	sort(e+1,e+n);
	for(int i=1;i<=m;i++)
	{
		ask[i].q=read();
		ask[i].id=i;
	}
	sort(ask+1,ask+m+1);
	for(int i=1;i<=n;i++) f[i]=i,sze[i]=1;
	int top=1;
	for(int i=1;i<=m;i++)//对于每一个询问
	{
		while(top<n&&e[top].z<=ask[i].q) merge(e[top].x,e[top].y),top++;//一直加边直到不超过当前询问的q
		ans[ask[i].id]=S;//将答案放回去
	}
	for(int i=1;i<=m;i++) cout<<ans[i]<<" ";
	return 0;
}

在线

/**************************************************************
 * Problem: CF1213G Path Queries-bcj-online
 * Author: Vanilla_chan
 * Date: 20210309
**************************************************************/
//预编译部分已略去
#define N 200010
int f[N],sze[N];
LL S;
int getf(int x)
{
	if(f[x]==x) return x;
	return f[x]=getf(f[x]);
}
LL calc(LL x)
{
	return x*(x-1)/2;
}
void merge(int x,int y)
{
	x=getf(x);
	y=getf(y);
	if(x==y) return;
	S-=calc(sze[x])+calc(sze[y]);
	if(sze[x]<sze[y])
	{
		f[x]=y;
		sze[y]+=sze[x];
		sze[x]=0;
	}
	else
	{
		f[y]=x;
		sze[x]+=sze[y];
		sze[y]=0;
	}
	S+=calc(sze[f[x]]);
	
}
int n,m;
struct edge
{
	int x,y,z;
	bool operator<(const edge& b)const
	{
		return z<b.z;
	}
}e[N];
LL ans[N];
int main()
{
	n=read();
	m=read();
	for(int i=1;i<n;i++)
	{
		e[i].x=read();
		e[i].y=read();
		e[i].z=read();
	}
	sort(e+1,e+n);
	for(int i=1;i<=n;i++) f[i]=i,sze[i]=1;
	int top=1;
	for(int i=1;i<=200000;i++)
	{
		while(top<n&&e[top].z<=i) merge(e[top].x,e[top].y),top++;
		ans[i]=S;
	}
	while(m--)
	{
		write(ans[read()]); 
	}
	return 0;
}
posted @ 2021-03-09 22:48  Vanilla_chan  阅读(70)  评论(0编辑  收藏  举报