建通道(位运算)

链接:https://ac.nowcoder.com/acm/contest/3003/I
来源:牛客网

题目描述

在无垠的宇宙中,有 n 个星球,第 i 个星球有权值 vi
由于星球之间距离极远,因此想在有限的时间内在星际间旅行,就必须要在星球间建立传送通道。
任意两个星球之间均可以建立传送通道,不过花费并不一样。第 i 个星球与第 j 个星球的之间建立传送通道的花费是 lowbit(vi⊕vj)
其中 ⊕ 为二进制异或,而 lowbit(x) 为 x 二进制最低位 1 对应的值,例如 lowbit(5)=1,lowbit(8)=8。特殊地,lowbit(0)=0
牛牛想在这 n 个星球间穿梭,于是――你需要告诉 牛牛,要使这 n 个星球相互可达,需要的花费最少是多少。

输入描述:

第一行,一个正整数 n 。
第二行,n 个非负整数 v1,v2,,vn 。
保证 1≤n≤2×1050≤vi<230

输出描述:

输出一行,一个整数表示答案。

输入

2
1 2

输出

1

说明

12 号点之间建立通道,v1⊕v2=3,lowbit(3)=1。

首先将权值去重(权值相等的点连接代价为 0 ),设去重后有 m 个点,接下来找到最小的二进制位 k ,满足存在 vi 的这个二进制位是 0 且存在 vj 的这个二进制位是  ,答案就是 2k×(m−1)

(相当于所有这位是 0 的点与 j 点连边,是 1 的点与 i 点连边)。

排序和去重以外时间复杂度 O(n+log⁡V),没有卡 O(nlog⁡V) ,好像两个 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
", ans);
29   return 0;
30 }
View Code
 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
");
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
",(1LL<<k)*(n-1));
49             return 0;
50         }
51     }
52     
53     return 0;
54 }

-

原文地址:https://www.cnblogs.com/jiamian/p/12275049.html