牛客练习赛15 F - 压状Kruskal

题目链接:点击打开链接

 

解题思路:大部分都在注释里,这个复杂度很玄学,应该是可以很强的测试数据,要不然我感觉要凉,感觉这个算法复杂度至少得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;
}

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值