Magic Matrix(CF632 F)(bitset,最小生成树)

7 篇文章 0 订阅
2 篇文章 0 订阅

题目

在这里插入图片描述

思路

我们可以看成是 n 2 n^2 n2 个点的完全图
发现对于三个点:
在这里插入图片描述
可以得到
{ a ≥ m a x ( b , c ) b ≥ m a x ( a , c ) c ≥ m a x ( a , b ) \begin{cases} a\ge max(b,c)\\ b\ge max(a,c)\\ c\ge max(a,b)\\ \end{cases} amax(b,c)bmax(a,c)cmax(a,b)
a ≥ b ≥ c a\ge b\ge c abc
可以得到 a = b a=b a=b
于是对于所有三元环而言最大值等于次大值

方法一(bitset)

我们可以从大到小加入边
假设现在加入 ( i , j ) (i,j) (i,j) 如果已经存在 ( i , k ) , ( k , j ) (i,k),(k,j) (i,k),(k,j) 就不行了
注意边权相同的一起判断,不要一个一个加
这是 O ( n 3 ) O(n^3) O(n3)
但可以用 b i t s e t bitset bitset 来优化 n 3 n^3 n3 暴力
我们可以看作检查 ( i , k ) , ( j , k ) (i,k),(j,k) (i,k),(j,k)
我们只需要 G [ i ] [ j ] G[i][j] G[i][j] 表示 ( i , j ) (i,j) (i,j) 是否连边
就判断 KaTeX parse error: Expected 'EOF', got '&' at position 5: G[i]&̲G[j] 即可
时间复杂度 O ( n 3 64 ) O(\frac{n^3}{64}) O(64n3)

方法二(最小生成树)

考虑加边时候必定是完全图考虑如果不是完全图:
在这里插入图片描述
找到一条即将加入的一条图中没有的边 ( i , j ) (i,j) (i,j) ,必然存在 ( i , k ) , ( k , j ) (i,k),(k,j) (i,k),(k,j) 的边,否则不连通
于是此时边的权值大于 ( i , k ) , ( k , j ) (i,k),(k,j) (i,k),(k,j) 并不满足三元环最大值等于次大值的条件
注意边权相同的一起判断,不要一个一个加
实现用 P r i m Prim Prim K r u s k a l Kruskal Kruskal 均可
时间复杂度 O ( n 2 ) O(n^2) O(n2) O ( n 2 l o g n ) O(n^2logn) O(n2logn)

代码

代码一

#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<ctime>
#include<queue>
#include<bitset>
#include<cstdio>
#include<vector>
#include<climits>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
int read(){
	bool f=0;int x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c=='-')f=1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return !f?x:-x;
}
#define MAXN 2500
#define INF 0x3f3f3f3f
bitset<MAXN+5> G[MAXN+5];
struct Edge{
	int u,v,w;
	friend bool operator < (Edge a,Edge b){return a.w<b.w;}
}edge[MAXN*MAXN+5];
int main(){
	int n=read();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			edge[(i-1)*n+j]=(Edge){i,j,read()};
	for(int i=1;i<=n;i++)
		if(edge[(i-1)*n+i].w!=0){
			puts("NOT MAGIC");
			return 0;
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<i;j++)
			if(edge[(i-1)*n+j].w!=edge[(j-1)*n+i].w){
				puts("NOT MAGIC");
				return 0;
			}
	sort(edge+1,edge+n*n+1);
	for(int i=1;i<=n*n;){
		int j=i;
		while(j<n*n&&edge[j+1].w==edge[i].w) j++;
		for(int t=i;t<=j;t++){
			int u=edge[t].u,v=edge[t].v;
			if(u==v) continue;
			if((G[u]&G[v]).count()>=1){
				puts("NOT MAGIC");
				return 0;
			}
		}
		for(int t=i;t<=j;t++){
			if(edge[t].u==edge[t].v) continue;
			G[edge[t].u][edge[t].v]=1;
		}
		i=j+1;
	}
	puts("MAGIC");
	return 0;
}

代码二

#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<ctime>
#include<queue>
#include<bitset>
#include<cstdio>
#include<vector>
#include<climits>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
int read(){
	bool f=0;int x=0;char c=getchar();
	while(c<'0'||'9'<c){if(c=='-')f=1;c=getchar();}
	while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
	return !f?x:-x;
}
#define MAXN 2500
#define INF 0x3f3f3f3f
struct Edge{
	int u,v,w;
	friend bool operator < (Edge a,Edge b){return a.w<b.w;}
}edge[MAXN*MAXN+5];
int fa[MAXN*MAXN+5];
int Find(int x){return fa[x]==x?x:(fa[x]=Find(fa[x]));}
int main(){
	int n=read();
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			edge[(i-1)*n+j]=(Edge){i,j,read()},fa[(i-1)*n+j]=(i-1)*n+j;
	for(int i=1;i<=n;i++)
		if(edge[(i-1)*n+i].w!=0){
			puts("NOT MAGIC");
			return 0;
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<i;j++)
			if(edge[(i-1)*n+j].w!=edge[(j-1)*n+i].w){
				puts("NOT MAGIC");
				return 0;
			}
	sort(edge+1,edge+n*n+1);
	for(int i=1;i<=n*n;){
		int j=i;
		while(j<n*n&&edge[j+1].w==edge[i].w) j++;
		for(int t=i;t<=j;t++){
			int fu=Find(edge[t].u),fv=Find(edge[t].v);
			if(edge[t].u==edge[t].v) continue;
			if(fu==fv){
				puts("NOT MAGIC");
				return 0;
			}
		}
		for(int t=i;t<=j;t++){
			int fu=Find(edge[t].u),fv=Find(edge[t].v);
			if(fu!=fv) fa[fv]=fu;
		}
		i=j+1;
	}
	puts("MAGIC");
	return 0;
}

思考

对于问题如果能转化为快速求交考虑 b i t s e t bitset bitset
这道题推出三元环和连通时是完全图的特点非常巧妙

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值