列表

详情


NC15616. 最小生成树

描述

给你一个无向的完全图,每个点都有一个特征值,若两点的特征值分别为u 和v,则这两点间的边的权重为u & v (& 就是按位与)。
给你一个正整数M,对于i = 0 ~ M - 1,告诉你此图中,特征值为i 的点有几个,请求出此图的最小生成树(Minimum Spanning Tree)上的边的权重和。

输入描述

输入共有 2 行。
第一行包含一个正整数 M,代表给定图中每个点的特征值都是小于 M 的非负整数。
第二行是一个长度为M 的字串S,此字串由数字字元组成(也就是'0' 至'9'),第i 个数字字元即代表特征值为 i-1 的点的个数。

输出描述

请输出一行包含一个整数,代表给定的图的最小生成树的边的权重和。

示例1

输入:

4
0514

输出:

4

说明:

此图共有 10 个点, 5 个点为特征值为 1,1 个点特征值为 2,还有 4 个点特征值为 3。
此图的其中一个最小生成树如下图,此生成数的权重和为 (1&2)×5+(3&1)×4=4

示例2

输入:

8
00090999

输出:

62

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++11(clang++ 3.9) 解法, 执行用时: 2279ms, 内存消耗: 2296K, 提交时间: 2018-04-18 18:50:03

#include<cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=(int)2e6;
int fa[N],num[N],m,ma[N];
char s[N];
ll ans;
int find(int x)
{
	return x==fa[x]? x:fa[x] = find(fa[x]);
}
void link(int x,int y)
{
	int a,b;
	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 (){
	scanf ("%d%s",&m,s);
	for (int i=0;i<m;i++){
		fa[i]=i;
		num[i]=s[i]-'0';
	}
	int inf=m-1;
	for(int i=0;i<m;i++){ 
		int x = i^inf;
		for(int j=x;;j=(j-1)&x){ 
			int y= j^i;
			if(num[y]){
				int z = y^inf;
				if(!ma[z^i]){
					for(int k=z;;k=(k-1)&z){
						int c= k^i;
						if(num[c])
							link(y,c);
						ma[c] = 1;
						if(!k) break;
					}
				} 
			}
			if(!j) break;
		} 
	}
	printf ("%lld\n",ans);
	
	return 0;
} 

上一题