【次小生成树】bzoj1977 [BeiJing2010组队]次小生成树 Tree
Description
小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值) 这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。
Input
第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。
Output
包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
Sample Input
1 2 1
1 3 2
2 4 3
3 5 4
3 4 3
4 5 6
Sample Output
HINT
数据中无向图无自环; 50% 的数据N≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。
题解
就是求严格次小生成树
目前常见做法是先用Kurskal求出最小生成树
然后枚举不在树上的边,试着把它连到树上,找到形成的环上边权最大但不等于新加的边权的一条边,计算出(最小生成树权值+新加边边权-找到的最大)
对每条不在最小生成树上的边计算过之后再取min就是结果
而要求形成的环上边权最大但不等于新加的边权的一条边,我们就可以用树剖维护一个最大值和次大值,然后就可以轻松(mlog2n)得出答案了
我才不会说我边权转化成点权的时候没按dfn序结果调了一整天也没发现呢,哼
代码
//by 减维 #include<cstdio> #include<iostream> #include<cstring> #include<queue> #include<cstdlib> #include<ctime> #include<cmath> #include<algorithm> #define ll long long #define ls l,mid,v<<1 #define rs mid+1,r,v<<1|1 #define getm mid=(l+r)>>1 using namespace std; struct us{ int x,y; ll v; }edg[300005]; struct edge{ int to,ne; ll v; }e[200005]; int n,m,num,ecnt,fa[100005],dep[100005],siz[100005],son[100005],val[100005]; int f[100005],dfn[100005],out[100005],head[100005],top[100005]; long long ans1,ans2,ans,ma[400005],ma2[400005]; bool pd[300005]; ll inf=1ll<<62; bool cmp(const us&x,const us&y){return x.v<y.v;} bool cm2(int x,int y){return x>y;} int find(int x) { if(x==fa[x])return x; fa[x]=find(fa[x]); return fa[x]; } void add(int x,int y,int z) { e[++ecnt].to=y; e[ecnt].ne=head[x]; e[ecnt].v=z; head[x]=ecnt; } void df1(int x) { dep[x]=dep[f[x]]+1; siz[x]=1; for(int i=head[x];i;i=e[i].ne) { int dd=e[i].to; if(dd==f[x])continue; f[dd]=x; df1(dd); siz[x]+=siz[dd]; if(!son[x]||siz[son[x]]<siz[dd]) son[x]=dd; } } void dfs(int x,int tp) { top[x]=tp; dfn[x]=++num; if(son[x])dfs(son[x],tp); for(int i=head[x];i;i=e[i].ne) { int dd=e[i].to; if(dd==f[x])continue; if(dd==son[x]){ val[dfn[dd]]=e[i].v; continue; } dfs(dd,dd); val[dfn[dd]]=e[i].v; } out[x]=num; } void upda(int v) { int x[5]; x[1]=ma[v<<1],x[2]=ma[v<<1|1],x[3]=ma2[v<<1],x[4]=ma2[v<<1|1]; sort(x+1,x+5,cm2); ma[v]=x[1]; ma2[v]=x[2]; } void print(int l,int r,int v) { if(l==r){ printf("%d %d\n",ma[v],ma2[v]); return ; } int mid;getm; print(ls); print(rs); } void build(int l,int r,int v) { if(l==r){ ma[v]=val[l]; ma2[v]=-inf; return ; } int mid;getm; build(ls); build(rs); upda(v); } ll ask(int l,int r,int v,int x,int y,int z) { if(r<x||y<l)return -inf; if(x<=l&&r<=y){ if(ma[v]!=z)return ma[v]; return ma2[v]; } int mid;getm; return max(ask(ls,x,y,z),ask(rs,x,y,z)); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;++i) scanf("%d%d%lld",&edg[i].x,&edg[i].y,&edg[i].v); sort(edg+1,edg+m+1,cmp); for(int i=0;i<=n;++i)fa[i]=i; int cntt=0; for(int i=1;i<=m;++i) { int x=edg[i].x,y=edg[i].y; int fx=find(x),fy=find(y); if(fx!=fy){ cntt++; pd[i]=1; fa[fx]=fa[fy]; ans1+=edg[i].v; add(x,y,edg[i].v); add(y,x,edg[i].v); } if(cntt==n-1)break; } df1(1); dfs(1,1); build(1,num,1); ans2=inf; for(int i=1;i<=m;++i) if(!pd[i]){ int x=edg[i].x,y=edg[i].y; ll tmp=-inf; while(top[x]!=top[y]) { if(dep[top[x]]>dep[top[y]]){ tmp=max(tmp,ask(1,num,1,dfn[top[x]],dfn[x],edg[i].v)); x=f[top[x]]; }else { tmp=max(tmp,ask(1,num,1,dfn[top[y]],dfn[y],edg[i].v)); y=f[top[y]]; } } int lca=dep[x]<dep[y]?x:y,deper=dep[x]>dep[y]?x:y; if(x!=y)tmp=max(tmp,ask(1,num,1,dfn[son[lca]],dfn[deper],edg[i].v)); ans2=min(ans2,ans1-tmp+edg[i].v); } printf("%lld",ans2); }
还有一种类型题,本质上还是求次小生成树
【题目背景】
HJZ 买了一套新房子,他正在为新房子的装修发愁。
【题目描述】
土豪 HJZ 的新房子需要铺设水管网。 水管网一共有 n 个点,其中 1 号点是进水口,其余 n − 1 个点是出水口。
房子预留 了 m 根水管的空间,每根水管可以连接两个点,但要花费一定的代价。
现在 HJZ 想知道让所有出水口都直接或间接与进水口联通的最小代价。
由于装修 过程中可能会出现一些玄学事件,他还想知道代价最小的连接方案是否唯一。保证存在 一种连接方案。
【输入格式】
从文件 pipe.in 中读入数据。
第一行一个正整数 T 表示测试数据组数。
对于每一组测试数据:
• 第一行两个整数 n, m 分别表示水管网的点数和预留的管道数。
• 接下来 m 行,每行三个正整数 a, b, c 表示一条连接 a, b 的双向管道需要 c 的 代价。
【输出格式】 输出到文件 pipe.out 中。 对于每一组测试数据,分别输出两行。
第一行一个整数表示最小代价。 第二行一个字符串 Yes 或 No 表示连接方案是否唯一。
这道题当时考试的时候一时脑抽。。。就写出来了一个二分。。。留在这里当做纪念吧。。。
//by 减维 #include<cstdio> #include<iostream> #include<cstring> #include<queue> #include<cstdlib> #include<ctime> #include<cmath> #include<map> #include<bitset> #include<algorithm> #define ll long long using namespace std; struct lin{ int fr,to,v; }ed[200005]; struct edge{ int to,ne,v; }e[200005]; struct node{ int v,pos; }a[100005]; ll ans; int t,n,m,num,tot,ecnt,head[100005],f[100005],fa[100005],lx[100005],rx[100005],son[100005],siz[100005],top[100005],dfn[100005],dep[100005]; bool fla,pd[200005]; bool cmp(const lin&x,const lin&y){return x.v<y.v;} bool cm2(const node&x,const node&y){ if(x.v==y.v)return x.pos<y.pos; return x.v<y.v; } void init() { ecnt=num=tot=0; ans=0; fla=0; memset(f,0,sizeof(f)); memset(pd,0,sizeof(pd)); memset(lx,0,sizeof(lx)); memset(rx,0,sizeof(rx)); memset(dep,0,sizeof(dep)); memset(son,0,sizeof(son)); memset(siz,0,sizeof(siz)); memset(dfn,0,sizeof(dfn)); memset(top,0,sizeof(top)); memset(head,0,sizeof(head)); } void add(int x,int y,int z) { e[++ecnt].to=y; e[ecnt].ne=head[x]; e[ecnt].v=z; head[x]=ecnt; } int find(int x) { if(x==fa[x])return x; fa[x]=find(fa[x]); return fa[x]; } void df1(int x) { siz[x]=1; dep[x]=dep[f[x]]+1; for(int i=head[x];i;i=e[i].ne) { int dd=e[i].to; if(dd==f[x])continue ; f[dd]=x; df1(dd); siz[x]+=siz[dd]; if(!son[x]||siz[son[x]]<siz[dd]) son[x]=dd; } } void dfs(int x,int tp) { top[x]=tp; dfn[x]=++num; if(son[x])dfs(son[x],tp); for(int i=head[x];i;i=e[i].ne) { int dd=e[i].to; if(dd==f[x]||dd==son[x])continue; dfs(dd,dd); } } void df2(int x,int v) { a[++tot].v=v; a[tot].pos=dfn[x]; for(int i=head[x];i;i=e[i].ne) { int dd=e[i].to; if(dd==f[x])continue; df2(dd,e[i].v); } } bool check(int x,int y,int v) { int l=lx[v],r=rx[v]; int mid; while(l<=r){ mid=(l+r)>>1; if(a[mid].pos<=y&&a[mid].pos>=x)return 1; else if(a[mid].pos>y)r=mid-1; else l=mid+1; } return 0; } int main() { freopen("pipe.in","r",stdin); freopen("pipe.out","w",stdout); scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); init(); for(int i=1;i<=m;++i)scanf("%d%d%d",&ed[i].fr,&ed[i].to,&ed[i].v); sort(ed+1,ed+m+1,cmp); for(int i=1;i<=n;++i)fa[i]=i; int cnt=0; for(int i=1,x,y,fx,fy;i<=m;++i) { x=ed[i].fr,y=ed[i].to; fx=find(x),fy=find(y); if(fx!=fy){ fa[fx]=fy; cnt++; ans+=(ll)ed[i].v; add(x,y,ed[i].v); add(y,x,ed[i].v); pd[i]=1; } if(cnt==n-1)break; } printf("%lld\n",ans); df1(1); dfs(1,1); df2(1,0); sort(a+1,a+tot+1,cm2); lx[a[1].v]=1; for(int i=1;i<=tot;++i) if(a[i].v!=a[i-1].v)lx[a[i].v]=i,rx[a[i-1].v]=i-1; rx[a[tot].v]=tot; for(int i=1,x,y,lca,v,deper;i<=m;++i) if(!pd[i]) { x=ed[i].fr,y=ed[i].to,v=ed[i].v; while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]])swap(x,y); if(check(dfn[top[x]],dfn[x],v)){ fla=1; break; } x=f[top[x]]; } if(x!=y){ lca=dep[x]<dep[y]?x:y; deper=lca==x?y:x; if(check(dfn[son[lca]],dfn[deper],v))fla=1; } if(fla)break; } if(fla)printf("No\n"); else printf("Yes\n"); } return 0; }