BestCoder Round #72 (div.1) Clarke and MST

来源:互联网 发布:riot.js 编辑:程序博客网 时间:2024/06/06 12:41
问题描述
克拉克是一名人格分裂患者。某一天克拉克变成了一名图论研究者。  他学习了最小生成树的几个算法,于是突发奇想,想做一个位运算and的最大生成树。  一棵生成树是由n−1n-1n1条边组成的,且nnn个点两两可达。一棵生成树的大小等于所有在生成树上的边的权值经过位运算and后得到的数。  现在他想找出最大的生成树。
输入描述
第一行是一个整数T(1≤T≤5)T(1 \le T \le 5)T(1T5),表示数据组数。  每组数据第一行是两个整数n,m(1≤n,m≤300000)n, m(1 \le n, m \le 300000)n,m(1n,m300000),分别表示点个数和边个数。其中n,m>100000n, m > 100000n,m>100000的数据最多一组。  接下来mmm行,每行333个整数x,y,w(1≤x,y≤n,0≤w≤109)x, y, w(1 \le x, y \le n, 0 \le w \le 10^9)x,y,w(1x,yn,0w109),表示x,yx, yx,y之间有一条大小为www的边。
输出描述
每组数据输出一行一个数,表示答案。若不存在生成树,输出000
输入样例
14 51 2 51 3 31 4 22 3 13 4 7
输出样例
1
</pre>
题目要求生成树相互&后的最大值。我们确定最后的ans的值不会大于(1<<30)[定会小于最大边的权值]。利用二进制思路,我们从最高位来枚举结果,若结果第i为1,那么我们就只需要考虑权值第i位为1的边,然后利用并查集判断这些边是否是生成树,若是,继续枚举(i-1)位,但是在符合第i位为1的前提下进行。因为结果是最大值。

/*  data:  3 3  1 2 1  1 3 11   2 3 10*/#include<stdio.h>#include<math.h>#include<stdlib.h>#include<algorithm>#include<string.h>#include<vector>using namespace std;typedef long long LL;const int M = 1e9;struct node{int ver;int vertex;int val;}edge[300010],tmp[300010];int fa[300010];int find(int x){if(fa[x] != x)fa[x] = find (fa[x]);return  fa[x];}void merge(int x,int y){int dx = find(x);int dy = find(y);if(dx != dy){fa[dx] = dy;}}int main(){int i,j,k,n,m,t,ans,mm;scanf("%d",&t);while(t--){ans = 0 ;scanf("%d%d",&n,&m);for(i=0;i<m;i++){scanf("%d%d%d",&edge[i].ver,&edge[i].vertex,&edge[i].val);}for(i=30;i>=0;i--){for(int i=1;i<=n;i++){     fa[i] = i;}int count;count = mm = 0 ;for(j=0;j<m;j++){//if(i==1) printf("i==1\n");//if((ans!=0 && ((1<<i) & ans)) || ans == 0)  // ans尽量大,所以第一次按最高位得到的ans定有用,但后面满足生成树还需判断if((1<<i) & edge[j].val){merge(edge[j].ver,edge[j].vertex);tmp[mm++] = edge[j];}}for(j=1;j<=n;j++){if(fa[j] == j){count ++;}}if(count == 1){m = mm;for(int i = 0;i<m;i++){edge[i] = tmp[i];}ans += (1<<i);}}printf("%d\n",ans);}return 0;}


借鉴http://blog.csdn.net/aozil_yang/article/details/50642966#reply

法二:找规律:

其实随便画几个就能看出规律来,

我直接说出规律了,,比如说N = 8的时候,

那么结果就是:

S1 = 1 + 2 + 3 + 4 + 5

S2 = 2 + 4 + 6 + 8

S3 = 3 + 6 + 9

S4 = 4 + 8

S5 = 5

S1 + S2 +... + S5 在加上N,就是结果!

规律很明显,很容易敲出代码了!


你可以在圆上给n个点进行标号,然后你从1号开始往后连线,直到最后,你会发现,能产生交点的只有2,3,4.。。。n-3,n-2位置的点才会产生交点。所以总共循环n-3次,所以n = 8 只要5次就够,s1,s2,s3..s5,就是对应的2号位置的连线,3号位置的连线,直到6号位置的连线!


#include<iostream>#include<cstdio>#include<set>#include<map>#include<vector>#include<cstring>#include<string>#include<sstream>#include<algorithm>#include<cmath>#include<cctype>#include<cstdlib>#define mem(x) memset(x,0,sizeof(x));#define mem1(x) memset(x,-1,sizeof(x));using namespace std;const int maxn = 1000 + 10;const int maxt = 75 + 10;const double eps = 1e-8;const int INF = 1e8;const double pi = acos(-1.0);typedef long long ll;typedef unsigned long long llu;int main(){    int n,T;    cin >> T;    while(T--){        cin >> n;        llu num = n-3;        llu sum = n;        for (int i = 1; i <= n-2; ++i){            sum += num*i+num*(num-1)/2*i;            num--;        }        cout << sum << endl;    }    return 0;}



0 0
原创粉丝点击