题目大意
一个图有
n
个点,
有
q
个询问,每次给定一个区间
1≤n≤102,1≤m≤105,1≤q≤1.5×104
题目分析
显然题目是求区间内的边的最小生成森林的边权和。
可以发现最小生成树(森林)具有可合并性。
一个很显然的想法:将所有边按照编号分块,设每一块大小为
B
,令
然后询问时直接拿出跨块的
f
值以及两边多出来的边合并一下就好了。
不过这个方法还是太naive了,我们把分块换成线段树就可以在
O(n(m+q)logmα(n))
的时间复杂度内解决问题。
代码实现
于是我依然特别naive地打了分块。
#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cctype>
#include <cmath>
using namespace std;
int read()
{
int x=0,f=1;
char ch=getchar();
while (!isdigit(ch)) f=ch=='-'?-1:f,ch=getchar();
while (isdigit(ch)) x=x*10+ch-'0',ch=getchar();
return x*f;
}
int buf[30];
void write(int x)
{
if (x<0) putchar('-'),x=-x;
for (;x;x/=10) buf[++buf[0]]=x%10;
if (!buf[0]) buf[++buf[0]]=0;
for (;buf[0];putchar('0'+buf[buf[0]--]));
}
const int N=105;
const int M=100005;
const int B=500;
int srt[M],id[M];
int fa[N],rank[N];
int mst[B][B][N];
int st[B],en[B];
int edg[M][3];
int n,m,q,cnt,bs,bcnt;
int getfather(int son){return fa[son]==son?son:fa[son]=getfather(fa[son]);}
void merge(int x,int y)
{
if (rank[x]<rank[y]) swap(x,y);
fa[y]=x,rank[x]+=rank[x]==rank[y];
}
bool cmp(int x,int y){return edg[id[x]][2]<edg[id[y]][2];}
int Kruscal(int *e)
{
e[0]=0;
int ret=0;
for (int i=1;i<=cnt;++i) srt[i]=i;
sort(srt+1,srt+1+cnt,cmp);
for (int i=1;i<=n;++i) fa[i]=i,rank[i]=0;
for (int i=1,j,x,y;i<=cnt;++i)
{
j=id[srt[i]],x=getfather(edg[j][0]),y=getfather(edg[j][1]);
if (x!=y) merge(x,y),ret+=edg[j][2],e[++e[0]]=j;
}
return ret;
}
void block()
{
bs=trunc(sqrt(m));
for (int l=1,r;l<=m;l=r+1)
{
st[++bcnt]=l,en[bcnt]=r=min(m,l+bs-1),cnt=0;
for (int i=l;i<=r;++i) id[++cnt]=i;
Kruscal(mst[bcnt][bcnt]);
}
for (int i=1;i<bcnt;++i)
for (int j=i+1;j<=bcnt;++j)
{
cnt=0;
for (int k=1;k<=mst[i][j-1][0];++k) id[++cnt]=mst[i][j-1][k];
for (int k=1;k<=mst[j][j][0];++k) id[++cnt]=mst[j][j][k];
Kruscal(mst[i][j]);
}
}
int main()
{
freopen("highway.in","r",stdin),freopen("highway.out","w",stdout);
n=read(),m=read(),q=read();
for (int i=1;i<=m;++i)
for (int j=0;j<3;++j)
edg[i][j]=read();
block();
for (int l,r,lid,rid;q--;printf("%d\n",Kruscal(mst[0][0])))
{
l=read(),r=read(),lid=rid=0,cnt=0;
for (int i=1;i<=bcnt;++i)
{
if (en[i]<l) continue;
if (st[i]>r) break;
if (st[i]<=l) for (int j=l;j<=en[i]&&j<=r;++j) id[++cnt]=j;
else if (en[i]>=r) for (int j=st[i];j<=r;++j) id[++cnt]=j;
else
{
if (!lid) lid=i;
rid=i;
}
}
if (lid) for (int i=1;i<=mst[lid][rid][0];++i) id[++cnt]=mst[lid][rid][i];
}
fclose(stdin),fclose(stdout);
return 0;
}