[JZOJ5060]公路建设

来源:互联网 发布:韩顺平javascript 编辑:程序博客网 时间:2024/06/05 09:40

题目大意

一个图有n个点,m条无向边,其中第i条边的权值是ci
q个询问,每次给定一个区间[li,ri],请你计算出只选择(编号在)这个区间内的边,在使图中连通块数目尽量小的前提之下,选择的边的权值和的最小值。

1n102,1m105,1q1.5×104


题目分析

显然题目是求区间内的边的最小生成森林的边权和。
可以发现最小生成树(森林)具有可合并性。
一个很显然的想法:将所有边按照编号分块,设每一块大小为B,令fi,j表示从第i个块到第j个块的边构成的最小生成森林的边集,这个可以O(n(mB)2)的时间复杂度完成。
然后询问时直接拿出跨块的f值以及两边多出来的边合并一下就好了。
Bm的话,时间复杂度可以做到O(nm+q(n+m)(log(n+m)+α(n)))(如果你实现得好一点,用归并排序可以省掉log)。
不过这个方法还是太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;}
0 0
原创粉丝点击