【次小生成树】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[];
- struct edge{
- int to,ne;
- ll v;
- }e[];
- int n,m,num,ecnt,fa[],dep[],siz[],son[],val[];
- int f[],dfn[],out[],head[],top[];
- long long ans1,ans2,ans,ma[],ma2[];
- bool pd[];
- ll inf=1ll<<;
- 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]]+;
- siz[x]=;
- 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[];
- x[]=ma[v<<],x[]=ma[v<<|],x[]=ma2[v<<],x[]=ma2[v<<|];
- sort(x+,x+,cm2);
- ma[v]=x[];
- ma2[v]=x[];
- }
- 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=;i<=m;++i)
- scanf("%d%d%lld",&edg[i].x,&edg[i].y,&edg[i].v);
- sort(edg+,edg+m+,cmp);
- for(int i=;i<=n;++i)fa[i]=i;
- int cntt=;
- for(int i=;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]=;
- fa[fx]=fa[fy];
- ans1+=edg[i].v;
- add(x,y,edg[i].v);
- add(y,x,edg[i].v);
- }
- if(cntt==n-)break;
- }
- df1();
- dfs(,);
- build(,num,);
- ans2=inf;
- for(int i=;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(,num,,dfn[top[x]],dfn[x],edg[i].v));
- x=f[top[x]];
- }else {
- tmp=max(tmp,ask(,num,,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(,num,,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[];
- struct edge{
- int to,ne,v;
- }e[];
- struct node{
- int v,pos;
- }a[];
- ll ans;
- int t,n,m,num,tot,ecnt,head[],f[],fa[],lx[],rx[],son[],siz[],top[],dfn[],dep[];
- bool fla,pd[];
- 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=;
- ans=;
- fla=;
- memset(f,,sizeof(f));
- memset(pd,,sizeof(pd));
- memset(lx,,sizeof(lx));
- memset(rx,,sizeof(rx));
- memset(dep,,sizeof(dep));
- memset(son,,sizeof(son));
- memset(siz,,sizeof(siz));
- memset(dfn,,sizeof(dfn));
- memset(top,,sizeof(top));
- memset(head,,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]=;
- dep[x]=dep[f[x]]+;
- 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)>>;
- if(a[mid].pos<=y&&a[mid].pos>=x)return ;
- else if(a[mid].pos>y)r=mid-;
- else l=mid+;
- }
- return ;
- }
- 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=;i<=m;++i)scanf("%d%d%d",&ed[i].fr,&ed[i].to,&ed[i].v);
- sort(ed+,ed+m+,cmp);
- for(int i=;i<=n;++i)fa[i]=i;
- int cnt=;
- for(int i=,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]=;
- }
- if(cnt==n-)break;
- }
- printf("%lld\n",ans);
- df1();
- dfs(,);
- df2(,);
- sort(a+,a+tot+,cm2);
- lx[a[].v]=;
- for(int i=;i<=tot;++i)
- if(a[i].v!=a[i-].v)lx[a[i].v]=i,rx[a[i-].v]=i-;
- rx[a[tot].v]=tot;
- for(int i=,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=;
- 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=;
- }
- if(fla)break;
- }
- if(fla)printf("No\n");
- else printf("Yes\n");
- }
- return ;
- }
【次小生成树】bzoj1977 [BeiJing2010组队]次小生成树 Tree的更多相关文章
- [BZOJ1977][BeiJing2010组队]次小生成树
题解: 首先要证明一个东西 没有重边的图上 次小生成树由任何一颗最小生成树替换一条边 但是我不会证啊啊啊啊啊啊啊 然后就很简单了 枚举每一条边看看能不能变 但有一个特殊情况就是,他和环上的最大值相等, ...
- [bzoj1977][BeiJing2010组队]次小生成树 Tree——树上倍增+lca
Brief Description 求一个无向图的严格次小生成树. Algorithm Design 考察最小生成树的生成过程.对于一个非树边而言,如果我们使用这一条非树边去替换原MST的路径上的最大 ...
- bzoj1977 [BeiJing2010组队]次小生成树 Tree
和倍增法求lca差不多,维护每个点往上跳2^i步能到达的点,以及之间的边的最大值和次大值,先求出最小生成树,对于每个非树边枚举其端点在树上的路径的最大值,如果最大值和非树边权值一样则找次大值,然后维护 ...
- bzoj1977 [BeiJing2010组队]次小生成树 Tree——严格次小生成树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1977 因为严格,所以要记录到 LCA 的一个次小值: 很快写好,然后改掉一堆错误后终于过了样 ...
- 【BZOJ1977】[BeiJing2010组队]次小生成树 Tree 最小生成树+倍增
[BZOJ1977][BeiJing2010组队]次小生成树 Tree Description 小 C 最近学了很多最小生成树的算法,Prim 算法.Kurskal 算法.消圈算法等等. 正当小 C ...
- BZOJ 1977: [BeiJing2010组队]次小生成树 Tree( MST + 树链剖分 + RMQ )
做一次MST, 枚举不在最小生成树上的每一条边(u,v), 然后加上这条边, 删掉(u,v)上的最大边(或严格次大边), 更新答案. 树链剖分然后ST维护最大值和严格次大值..倍增也是可以的... - ...
- 1977: [BeiJing2010组队]次小生成树 Tree
1977: [BeiJing2010组队]次小生成树 Tree https://lydsy.com/JudgeOnline/problem.php?id=1977 题意: 求严格次小生成树,即边权和不 ...
- [BeiJing2010组队]次小生成树 Tree
1977: [BeiJing2010组队]次小生成树 Tree Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 5168 Solved: 1668[S ...
- 【刷题】BZOJ 1977 [BeiJing2010组队]次小生成树 Tree
Description 小 C 最近学了很多最小生成树的算法,Prim 算法.Kurskal 算法.消圈算法等等. 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了.小 P 说,让小 C 求出一 ...
随机推荐
- Django配置session
在settings.py文件里加入 #配置失效时间为半个小时 SESSION_COOKIE_AGE = 60*30 #关闭浏览器清除cookie SESSION_EXPIRE_AT_BROWSER_C ...
- ajax初探--实现简单实时验证
学习技术最好的方式就是在做中学,做一个小demo来对前端输入进行实时验证. 利用ajax技术和Sevlet技术来实现,使用原生的js. 源码可访问,我的Github 什么是ajax Ajax 即&qu ...
- 基于 HTML5 Canvas 的 3D 机房创建
对于 3D 机房来说,监控已经不是什么难事,不同的人有不同的做法,今天试着用 HT 写了一个基于 HTML5 的机房,发现果然 HT 简单好用.本例是将灯光.雾化以及 eye 的最大最小距离等等功能在 ...
- 在非controllers中获取httpServletRequest
HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()) ...
- 初探Azure的保留实例(Reserved Instance)
最近的Ignite 2017宣布了Azure将在年底推出保留实例(Reserved Instance).虽然在没有RI的这些年,Azure的EA Monetary Commitment同样也提供了和R ...
- SoapUI模拟soap接口返回不同响应(通过groovy脚本)
一.创建soap项目,输入wsdl文件,然后生成SOAP Mock Service,再生成测试用例,然后新建新的响应 WSDL文件:MathUtil.wsdl <?xml version=&qu ...
- xamarin android menu的用法
在Android中的菜单有如下几种: OptionMenu:选项菜单,android中最常见的菜单,通过Menu键来调用 SubMenu:子菜单,android中点击子菜单将弹出一个显示子菜单项的悬浮 ...
- Java SE 8 流库(三)
1.7. Optional类型 容器对象,可能包含或不包含非空值.如果存在一个值,isPresent()将返回true,get()将返回值.还提供了依赖于包含值是否存在的附加方法,如orElse()( ...
- Docker入门书籍
https://yuedu.baidu.com/ebook/d817967416fc700abb68fca1 精细讲解,入门使用极佳.
- date 命令详解
date - print or set the system date and time Display the current time in the given FORMAT, or set th ...