建通道(位运算)
建通道(位运算)
链接:https://ac.nowcoder.com/acm/contest/3003/I
来源:牛客网
题目描述
在无垠的宇宙中,有 n 个星球,第 i 个星球有权值 vi。
由于星球之间距离极远,因此想在有限的时间内在星际间旅行,就必须要在星球间建立传送通道。
任意两个星球之间均可以建立传送通道,不过花费并不一样。第 i 个星球与第 j 个星球的之间建立传送通道的花费是 lowbit(vi⊕vj)。
由于星球之间距离极远,因此想在有限的时间内在星际间旅行,就必须要在星球间建立传送通道。
任意两个星球之间均可以建立传送通道,不过花费并不一样。第 i 个星球与第 j 个星球的之间建立传送通道的花费是 lowbit(vi⊕vj)。
其中 ⊕ 为二进制异或,而 lowbit(x) 为 x 二进制最低位 1 对应的值,例如 lowbit(5)=1,lowbit(8)=8。特殊地,lowbit(0)=0。
牛牛想在这 n 个星球间穿梭,于是――你需要告诉 牛牛,要使这 n 个星球相互可达,需要的花费最少是多少。
牛牛想在这 n 个星球间穿梭,于是――你需要告诉 牛牛,要使这 n 个星球相互可达,需要的花费最少是多少。
输入描述:
第一行,一个正整数 n 。
第二行,n 个非负整数 v1,v2,…,vn 。
保证 1≤n≤2×105,0≤vi<230。
输出描述:
输出一行,一个整数表示答案。
输入
2 1 2
输出
1
说明
1、2 号点之间建立通道,v1⊕v2=3,lowbit(3)=1。
首先将权值去重(权值相等的点连接代价为 0 ),设去重后有 m 个点,接下来找到最小的二进制位 k ,满足存在 vi 的这个二进制位是 0 且存在 vj 的这个二进制位是 ,答案就是 2k×(m−1)
(相当于所有这位是 0 的点与 j 点连边,是 1 的点与 i 点连边)。
排序和去重以外时间复杂度 O(n+logV),没有卡 O(nlogV) ,好像两个 log 也过了。
标程:
1 #include <cstdio> 2 #include <algorithm> 3 using namespace std; 4 const int N = 2e5 + 5; 5 int n, k; 6 int v[N], va, vo; 7 long long ans; 8 int main() { 9 scanf("%d", &n); 10 for (int i = 1; i <= n; i++) 11 scanf("%d", &v[i]); 12 sort(v + 1, v + n + 1); 13 va = 0x7fffffff; 14 for (int i = 1; i <= n; i++) { 15 va &= v[i]; 16 vo |= v[i]; 17 } 18 for (int i = n; i; i--) 19 k += (v[i] != v[i + 1]); 20 va ^= vo; 21 for (int i = 0; i <= 30; i++) { 22 int cur = 1 << i; 23 if (va & cur) { 24 ans = 1ll * cur * (k - 1); 25 break; 26 } 27 } 28 printf("%lld\n", ans); 29 return 0; 30 }
1 #include <stdio.h> 2 #include <string.h> 3 #include <iostream> 4 #include <string> 5 #include <math.h> 6 #include <algorithm> 7 #include <vector> 8 #include <stack> 9 #include <queue> 10 #include <set> 11 #include <map> 12 #include <sstream> 13 const int INF=0x3f3f3f3f; 14 typedef long long LL; 15 const int mod=1e9+7; 16 const int maxn=1e4+10; 17 using namespace std; 18 19 int a[200005]; 20 21 int main() 22 { 23 #ifdef DEBUG 24 freopen("sample.txt","r",stdin); 25 #endif 26 27 int n; 28 scanf("%d",&n); 29 for(int i=1;i<=n;i++) 30 scanf("%d",&a[i]); 31 sort(a+1,a+1+n); 32 n=unique(a+1,a+1+n)-(a+1);//去重 33 if(n==1) 34 { 35 printf("0\n"); 36 return 0; 37 } 38 for(int k=0;k<=n;k++) 39 { 40 int t0=0,t1=0; //在第i位上标记是否含有0 1 41 for(int i=1;i<=n;i++) 42 { 43 if(a[i]&(1<<k)) t1++; 44 else t0++; 45 } 46 if(t0&&t1) //如果两种情况都有那么可以建立n-1条边 47 { 48 printf("%d\n",(1LL<<k)*(n-1)); 49 return 0; 50 } 51 } 52 53 return 0; 54 }
-