AT2134 Zigzag MST 最小生成树

正解:最小生成树

解题报告:

先放下传送门QAQ

然后这题,首先可以发现这神奇的连边方式真是令人头大,,,显然要考虑转化掉QAQ

大概看一下可以发现点对的规律是,左边++,交换位置,再仔细想下,就每个点会连上相邻两点,也就相邻两点会通过另外一个点连边

首先可以发现加到后来已经是麻油意义的了,想下kruscal的意义,当两条边的两端是一样的那显然权值大的那条边麻油意义的,就是说每次最多加n条边

这时候再结合prim,可以发现我们每次加入一个不在联通块的点的时候我们一点也不关心它和哪个点相连的,只要知道和联通块的最短距离多少就好

所以如果有(a,b,c),(b,a+1,c+1),考虑到ab早晚在一个联通块中的,所以可以直接当做是(a,a+1,c+1)

不难想到这样把所有边都处理完之后得到的就是一堆[(a,a+1),w]的边了(这儿这么写的意义是说a和a+1是固定的然而对应了很多w

于是再递推两遍(考虑到环所以是两遍呢QAQ)得到所有(a,a+1)唯一的w,这样就变成了一棵新树,再跑遍最小生成树就好QAQ

然后上面是想法,但是这个想法有一个问题昂,就是它的边还是太多了,所以考虑怎么再优化

可以考虑差分,开个mn[]存当前节点的min,对每个修改就只要修改端点就成

然后最后扫一圈,新的边权就是min(mn[i],mn[i-1]+1)

然后再仔细想下发现,因为它是环,所以要扫两次

然后就做完辣辣辣!

顺便说下,这题非常好地体现了关于最小生成树的两种常见解题策略——去除不可能的边&在不影响答案的情况下改边

#include<bits/stdc++.h>
using namespace std;
#define il inline
#define ll long long
#define rg register
#define gc getchar()
#define rp(i,x,y) for(rg int i=x;i<=y;++i)

const int N=200000+10;
int n,q,fa[N];
ll mn[N<<1],as;
struct ed{int fr,to;ll wei;};
vector<ed>edge;

il int read()
{
    rg char ch=gc;rg int x=0;rg bool y=1;
    while(ch!='-' && (ch>'9' || ch<'0'))ch=gc;
    if(ch=='-')ch=gc,y=0;
    while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc;
    return y?x:-x;
}
il ll readl()
{
    rg char ch=gc;rg ll x=0;rg bool y=1;
    while(ch!='-' && (ch>'9' || ch<'0'))ch=gc;
    if(ch=='-')ch=gc,y=0;
    while(ch>='0' && ch<='9')x=(x<<1)+(x<<3)+(ch^'0'),ch=gc;
    return y?x:-x;
}
il bool cmp(ed x,ed y){return x.wei<y.wei;}
il int fd(int x){return fa[x]==x?x:fa[x]=fd(fa[x]);}


int main()
{
//     freopen("zm.in","r",stdin);freopen("zm.out","w",stdout);
    n=read();q=read();memset(mn,127/3,sizeof(mn));
    while(q--){int a=read()%n+1,b=read()%n+1;ll c=readl();mn[a]=min(mn[a],c+1);mn[b]=min(mn[b],c+2);edge.push_back((ed){a,b,c});}
    rp(i,1,n<<1)mn[i]=min(mn[i],mn[i-1]+2);rp(i,1,n)edge.push_back((ed){i,i%n+1,min(mn[i],mn[i+n])});sort(edge.begin(),edge.end(),cmp);
    rp(i,1,n)fa[i]=i;int sz=edge.size();rp(i,0,sz-1){int fafr=fd(edge[i].fr),fato=fd(edge[i].to);if(fafr^fato)as+=edge[i].wei,fa[fafr]=fato;}
    printf("%lld\n",as);
    return 0;
}
然后放代码!overr!
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值