您的位置:首页 > 其它

BZOJ 3714 [PA2014]Kuglarz - 最小生成树模型

2017-11-16 21:51 375 查看
传送门

题解:

首先不难发现,如果知道了[x,y]和[y+1,z]就可以知道[x,z],或者如果知道了[x,y]和[z,y]就可以知道[min(x,z),max(x,z)-1]。

最后是要知道所有的[x,x],其中1<=x<=n。

发现一个一个选要选n次。按照上述方法,可以证明也要至少选n次区间才可以知道每一个数字是多少。

(同时题目里面的奇偶性是没什么用的……)

且如果选了超过n次的话,可以证明一定有没有必要的操作。

将区间[x,y]记为[x,y+1),那么[x,y)和[y,z)可以合并成[x,z),以及如果x<z的话[x,y)和[z,y)可以合并成[x,z)。

知道了区间[x,y),意味着如果还知道了以x为端点的另一个区间的信息(无论此时x是左端点还是右端点),通过y也能得到一些信息。

也就是说知道了[x,y)后x和y的信息共享,用并查集连接起来。连接n次(因为一共有n+1个点)后就能确保是答案。

其实本质就是最小生成树,即将cij转为(i,j+1,cij)的边即可。理论上写一个prim最好,但是习惯写了kruskal……

反正最后也跑过了。

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define lint long long
#define N 2010
#define M N*N
using namespace std;
struct edges{
int u,v,w;
}e[M];int m,fa
;
inline int add_edge(int u,int v,int w)
{
return m++,e[m].u=u,e[m].v=v,e[m].w=w;
}
inline bool ecmp(const edges &e1,const edges &e2)
{
return e1.w<e2.w;
}
inline int findf(int x)
{
int fx=x,y;
while(fx^fa[fx]) fx=fa[fx];
while(x^fx) y=fa[x],fa[x]=fx,x=y;
return fx;
}
#define gc getchar()
inline int inn()
{
int x;char c;
while((c=gc)<'0'||c>'9');
x=c^'0';
while((c=gc)>='0'&&c<='9')
x=(x<<1)+(x<<3)+(c^'0');
return x;
}
#undef gc
int main()
{
int n;scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)
add_edge(i,j+1,inn());
sort(e+1,e+m+1,ecmp),n++;
for(int i=1;i<=n;i++) fa[i]=i;
int cnt=0;lint ans=0LL;
for(int i=1;i<=m&&cnt<n-1;i++)
{
int fx=findf(e[i].u),fy=findf(e[i].v);
if(fx^fy) fa[fx]=fy,ans+=e[i].w,cnt++;
}
printf("%lld\n",ans);return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: