题目链接:点击打开链接
解题思路:大部分都在注释里,这个复杂度很玄学,应该是可以很强的测试数据,要不然我感觉要凉,感觉这个算法复杂度至少得O(n*(n-(2^(按位算1的个数))))
#include<bits/stdc++.h>
using namespace std;
const int mx = 1e5 + 5e4;
typedef long long ll;
int n,num[mx],fa[mx],a,b;
char str[mx];
ll ans;
bool vis[mx];
int find(int x)
{
return x==fa[x]? x:fa[x] = find(fa[x]);
}
void Uinon(int x,int y)
{
a = find(x),b = find(y);
if(a!=b){
ans += ll(x&y)*(num[x]+num[y]-1);//因为从小到大排,所以他们肯定是互连的,不存在和其他连的可能
num[x] = num[y] = 1;
fa[b] = a;
}
}
int main()
{
//对于两个非负整数a,b,若(a&b)= a,则称a是b的「位元子集元素」且b是a的「位元父集元素」。
scanf("%d%s",&n,str);
for(int i=0;i<n;i++) num[i] = str[i]-'0',fa[i] = i;
int INF = n - 1,mask1,mask2,x,y,c;
for(int i=0;i<n;i++){//根据Kruskal算法从小到大枚举边权值
mask1 = i^INF;//i的补集
for(int j=mask1;;j=(j-1)&mask1){
x = j^i;枚举i的位元父集
if(num[x]){
y = x^INF;//x的补集+i
if(!vis[y^i]){//已经连上了就不用考虑了
for(int k=y;;k=(k-1)&y){//枚举与x按位&是i的
c = k^i;
if(num[c])
Uinon(x,c);
vis[c] = 1;
if(!k) break;
}
}
}
if(!j) break;
}
}
printf("%lld\n",ans);
return 0;
}