题目链接:点击查看
题目大意:给出每个点的权值为v[ i ],现在要求将 n 个点互相连通,以保证代价最小,点 i 和点 j 连接的代价为 lowbit( v[ i ] ^ v[ j ])
题目分析:挺简单的一道思维题吧,比赛的时候最后开的这个题目,时间不太多了,心态也比较浮躁,导致没有发挥好,思路倒是挺正确的,但是实现上有点小问题,导致最后一刻也没有AC,其实读完题的第一反应是最小生成树,但显然建图是不合适的,因为数据太大了,所以我们需要贪心的去想如何连接比较合适,因为lowbit函数的存在,加上异或,我们只需要考虑每个节点的最小的那个位置的 1 就好了,其实读完题后显然相同权值的结点可以直接连接,代价为0,所以我们在输入的时候就可以顺便去重,对于剩下的结点,其代价肯定是两两互不相同的,我们只需要找到一个位置,在二进制上,对于每个点在这个位置上的表达既有 1 ,又有 0,并且这个二进制的位置最小,找到这样一个位置后,我们只需要让所有这个位置为 1 的结点去与为 0 的结点建边,反过来也是一样,这样就是最优解了,答案为此时结点的个数减一乘以这个二进制
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<unordered_map>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=2e5+100;
unordered_map<int,bool>vis;
int n;
vector<int>a;
bool check(int base)
{
bool _1=false,_0=false;
for(int i=0;i<a.size();i++)
{
if(a[i]&base)
_1=true;
else
_0=true;
}
return _1&&_0;
}
int main()
{
//#ifndef ONLINE_JUDGE
// freopen("input.txt","r",stdin);
//#endif
// ios::sync_with_stdio(false);
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int num;
scanf("%d",&num);
if(vis[num])
continue;
vis[num]=true;
a.push_back(num);
}
if(a.size()==1)//如果所有数都相同
return 0*printf("0\n");
int base=1;
while(!check(base))
base<<=1;
printf("%lld\n",1LL*(a.size()-1)*base);
return 0;
}