[HDOJ 4896] Minimal Spanning Tree [最小生成树]

来源:互联网 发布:高仿耐克鞋淘宝店 编辑:程序博客网 时间:2024/06/06 01:13

题目给出了一个图,每个点和他的前后5个点连边,边的权值是由一个公式计算得出的。求最小生成树的边权和。

我们观察公式,发现其有一个循环节,为54。就是说点i上的边的权值和点i+54上的边的权值是完全相同的。然后我们就可以把这个图缩成5+54+54+53+5这么多个点的图,求最小生成树了。

左右的5是因为这5个点和其他点不同,其左右不是都有5条边。第一个54为添加不需重复的边准备,即可能有一些边是不需要和循环节一起重复很多次的。第二个54为循环节的54。后边的53为剩余的53,即总点数减去10然后模54的余数。

然后就是求最小生成树,对边排序后按序添加。如果两条边权值一样,那么将不是循环节的那条(即不会被复制多次的)放在前边,求最小生成树之后计算结果的时候,要将是循环节的边乘以循环节的个数,即拷贝多份。

标准解法不是这样..是DP+矩阵加速...现在这个解法我没有严格的证明..不过感觉上是对的,并且也A了...

#include <cstdio>#include <algorithm>#include <iostream>using namespace std;const int mod=2333333;const int XX=5;inline bool notin(int i) {if (i<=XX+54||i>XX+54+54) return true;return false;}struct Edge {int x,y;long long v;void clear(int xx,int yy,long long vv) {x=xx;y=yy;v=vv;}friend bool operator < (const Edge &a,const Edge &b) {if (a.v!=b.v) return a.v<b.v;if (notin(a.x)) return true;return false;}};struct DisjoinSet {int a[600];void clear(int n) {for (int i=1;i<=n;i++) a[i]=i;}int get(int i) {if (a[i]==i) return i;return a[i]=get(a[i]);}void tosame(int x,int y) {x=get(x);y=get(y);a[x]=y;}};DisjoinSet c;Edge b[1000];int bp,n,pp;long long ans;int main() {int i,j,seed;while (scanf("%d%d",&n,&seed)!=EOF) {pp=1;while (n-54>=54+54+XX+XX) {n-=54;pp++;}c.clear(n);bp=0;long long x=seed;for (i=2;i<=n;i++) {x=x*907%mod;long long t=x;for (j=max(1,i-5);j<i;j++) {x=x*907%mod;long long w=t^x;b[bp++].clear(i,j,w);}}sort(b,b+bp);ans=0;for (i=0;i<bp;i++) {if (c.get(b[i].x)!=c.get(b[i].y)) {c.tosame(b[i].x,b[i].y);if (notin(b[i].x)) ans+=b[i].v;else ans+=b[i].v*pp;}}printf("%lld\n",ans);}return 0;}



0 0
原创粉丝点击