BZOJ 3714 [PA2014]Kuglarz

来源:互联网 发布:比较实用的软件 编辑:程序博客网 时间:2024/05/17 15:05

传送门

题解:

首先不难发现,如果知道了[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*Nusing namespace std;struct edges{int u,v,w;}e[M];int m,fa[N];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 gcint 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;}


原创粉丝点击