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;
}