DTOJ#4067. 鸡(ji)

和丢人的鸭子与狗不同,鸡是一位伟大的计算机科学家,他正在处理一个经典问题:最小生成树问题。

现在,有 n n n 位选手回答了 m m m个选择题,每个选择题都只能选 L L L 或者 R R R,也就是说,每个选手都会给出一个 m m m 位的 L R LR LR 串。鸡将选手看作了点,两个点之间的边权即为两位选手有多少个回答不同。现在,鸡想要你求出这张图的最小生成树。

思路:
容易想到枚举边权,但这样复杂度上限为 O ( 2 m × n ) O(2^m\times n) O(2m×n)。虽然数据过水导致我过了。。
这时我们想到这样会浪费很多本就不可能到达的边,于是我们一步步扩展可能边。
做法为类似bfs,扩展 a [ i ] a[i] a[i],保存 2 m 2^m 2m 中所有数第一次被访问到的值。
第二次访问就连边。同时可以证明bfs中不同层中上一层的边一定比下一层优。于是我们每跑完一层就执行一遍最小生成树。
时间复杂度 O ( n l o g n ) O(nlogn) O(nlogn)

#include<bits/stdc++.h>
#define N 200005
using namespace std;
int read(){
    
    
	int op=1,sum=0;char ch=getchar();
	while(ch<'0'||ch>'9') {
    
    if(ch=='-') op=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') sum=(sum<<3)+(sum<<1)+ch-'0',ch=getchar();
	return op*sum;
}
int n,m,a[N];
int fa[N],fr[1<<20],d[1<<20];
inline int get(int x){
    
    return fa[x]^x?fa[x]=get(fa[x]):x;}
queue<int> q;
struct node{
    
    
	int x,y,w;
}e[1<<22];
bool cmp(node a,node b){
    
    return a.w<b.w;}
int et,res,ans;
inline void solve(){
    
    
	sort(e+1,e+1+et,cmp);
	for(int i=1;i<=et;++i){
    
    
		int x=e[i].x,y=e[i].y,fx=get(x),fy=get(y);
		
		if(fx^fy){
    
    
			fa[fx]=fy;ans+=e[i].w;--res;
		}
	}
	et=0;
}
void bfs(){
    
    
	memset(d,0,sizeof(d));
	memset(fr,0,sizeof(fr));
	for(int i=1;i<=n;++i)fa[i]=i;
	ans=0;res=0;
	while(!q.empty())q.pop();
	for(int i=1;i<=n;++i)if(!d[a[i]]){
    
    d[a[i]]=1,fr[a[i]]=i,q.push(a[i]);++res;}
//	cout<<res<<endl;
	int las=0;
	while(!q.empty()){
    
    
		int top=q.front();q.pop();
		//cout<<top<<endl;
		if(d[top]^las){
    
    
			//cout<<"L:"<<et<<endl;
			solve();las=d[top];
			if(res==1){
    
    
				return ;
			}
		}
		int fx=get(fr[top]),fy;
		for(int i=0;i<m;++i){
    
    
			int to=(top^(1<<i));
			//cout<<to<<" "<<top<<" "<<d[to]<<" "<<et<<endl;
			if(d[to]){
    
    
				fy=get(fr[to]);
				//cout<<fy<<" "<<fx<<endl;
				if(fx^fy){
    
    e[++et].x=fx,e[et].y=fy,e[et].w=d[to]+d[top]-1;}
			}else{
    
    
				d[to]=d[top]+1;fr[to]=fr[top],q.push(to);
			}
		}
	}
	solve();
//	for(int i=0;i<1<<m;++i)cout<<d[i]<<" ";cout<<endl;
}
char s[25];
int main(){
    
    
	int T=read();
	while(T--){
    
    
		m=read(),n=read();
		for(int i=1;i<=n;++i){
    
    
			scanf("%s",s);
			a[i]=0;
			for(int j=0;j<m;++j){
    
    
				a[i]|=(s[j]=='R')<<j;
			}
			//cout<<a[i]<<endl;
		}
		bfs();
		printf("%d\n",ans);
	}
	return 0;
}/*
1
4 5
LRRL
RRRR
RLLL
LLLL
RLRL

*/

猜你喜欢

转载自blog.csdn.net/CSDNzhanghongyu/article/details/110500086
今日推荐