Codeforces 632F - Magic Matrix(暴力 bitset or Prim 求最小生成树+最小瓶颈路)
开始挖老祖宗(ycx)留下来的东西.jpg
本来想水一道紫题作为 AC 的第 500 道紫题的,结果发现点开了道神题。
首先先讲一个我想出来的暴力做法。条件一和条件二直接扫一遍判断掉。先将所有点按照 \(a_{i,j}\) 按权值大小从小到大排序并依次插入这些点,我们实时维护一个 \(n\times n\) 的 bool 数组 \(vis\),\(vis_{i,j}\) 表示第 \(i\) 行第 \(j\) 列的数是否被访问了。当我们插入某个 \(a_{i,j}\) 时,如果 \(\exist k\in[1,n]\) 使得 \(vis_{i,k}=vis_{j,k}=1\),那么意味着 \(a_{i,k},a_{j,k}\) 均小于 \(a_{i,j}\),也就不符合条件三了。这个将 bool 数组换成 bitset 可以实现 \(\mathcal O(\dfrac{n}{\omega})\) 判断。还有一个小问题,那就是如果有权值重复的,比如说 \(a_{i,j}=a_{i',j'}\),假设 \(a_{i',j'}\) 比 \(a_{i,j}\) 后访问,那么在访问 \(a_{i',j'}\) 的时候 \(vis_{i,j}\) 已经等于 \(1\) 了。如果在这种情况下 \(i'=i\),并且 \(vis_{j',j}\) 刚好等于 \(1\),那么程序就会认为这种情况不满足条件三,而实际上 \(\max(a_{i,j},a_{j',j})=a_{i',j'}\)。这个问题也异常容易解决,直接 two pointers 扫描一遍权值相同的数,对于这些数,先一起判断掉再一起插入就行了。
时间复杂度 \(\dfrac{n^3}{\omega}\),荣膺最劣解。
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=1;
while(!isdigit(c)){if(c=='-') neg=-1;c=getchar();}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
x*=neg;
}
const int MAXN=2500;
int n,a[MAXN+5][MAXN+5];pair<int,pii> p[MAXN*MAXN+5];
bitset<MAXN+5> bt[MAXN+5];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){
scanf("%d",&a[i][j]);p[(i-1)*n+j]=mp(a[i][j],mp(i,j));
} sort(p+1,p+n*n+1);
for(int i=1;i<=n;i++) if(a[i][i]) return printf("NOT MAGIC\n"),0;
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(a[i][j]!=a[j][i])
return printf("NOT MAGIC\n"),0;
for(int l=1,r=1;l<=n*n;l=r){
while(p[r].fi==p[l].fi&&r<=n*n){
int x=p[r].se.fi,y=p[r].se.se;
if((bt[x]&bt[y]).count()) return printf("NOT MAGIC\n"),0;
r++;
} for(int i=l;i<r;i++){int x=p[i].se.fi,y=p[i].se.se;bt[x][y]=1;}
} printf("MAGIC\n");
return 0;
}
事实上我们发现这个东西和图论关系非常密切。如果我们把这个矩阵看作邻接矩阵,那么条件一意味着图中不存在自环,条件二意味着 \(w_{u,v}=w_{v,u}\),也就是说满足条件一和条件二的矩阵是一张无向带权完全图的邻接矩阵。我们再来分析条件三。比方说有四个点 \(i,j,k,l\),根据条件三 \(w_{i,k}\leq\max(w_{i,j},w_{j,k}),w_{i,l}\leq\max(w_{i,k},w_{k,l})\leq\max\{w_{i,j},w_{j,k},w_{k,l}\}\),这意味着 \(\forall i,j\in[1,n]\),任意一条 \(i,j\) 路径上的边权的最大值 \(\geq w_{i,j}\)。故 \(\forall i,j\in[1,n]\),\(i,j\) 之间路径上最大权值的最小值 \(\geq w_{i,j}\)。看到这个条件很容易想到最小瓶颈路——构建出最小生成树,那么 \(i,j\) 之间路径上最大权值的最小值就是最小生成树上 \(i,j\) 路径上权值的最大值。于是求一遍最小生成树,然后对于每个点进行一遍 DFS 求出它到每个点的路径上权值的最大值就行了。注意到这张图是一张稠密图,\(m=n^2\),故 Kruskal、加堆优化的 Prim 都是 \(m\log n=n^2\log n\) 的,而不加堆优化的 Prim 反而是 \(n^2\) 的,故此题用 Prim 效率反而更高。
然而这才是我第二次写 Prim(第一次是两年以前) 啊……习惯了 Kruskal 的我都忘了 Prim 咋写了……
code(Kruskal):
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=1;
while(!isdigit(c)){if(c=='-') neg=-1;c=getchar();}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
x*=neg;
}
const int MAXN=2500;
int n,a[MAXN+5][MAXN+5],f[MAXN+5];
pair<int,pii> p[MAXN*MAXN+5];
int find(int x){return (!f[x])?x:f[x]=find(f[x]);}
bool merge(int x,int y){
x=find(x);y=find(y);
if(x!=y) return f[x]=y,1;
return 0;
}
int hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],ec=0;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
void dfs(int x,int rt,int f,int mx){
if(f&&a[x][rt]>mx){printf("NOT MAGIC\n");exit(0);}
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f) continue;
dfs(y,rt,x,max(mx,a[x][y]));
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++){
scanf("%d",&a[i][j]);p[(i-1)*n+j]=mp(a[i][j],mp(i,j));
}
for(int i=1;i<=n;i++) if(a[i][i]) return printf("NOT MAGIC\n"),0;
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(a[i][j]!=a[j][i])
return printf("NOT MAGIC\n"),0;
sort(p+1,p+n*n+1);
for(int i=1;i<=n*n;i++) if(merge(p[i].se.fi,p[i].se.se))
adde(p[i].se.fi,p[i].se.se),adde(p[i].se.se,p[i].se.fi);
for(int i=1;i<=n;i++) dfs(i,i,0,-1);
printf("MAGIC\n");
return 0;
}
code(Prim):
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
template<typename T> void read(T &x){
x=0;char c=getchar();T neg=1;
while(!isdigit(c)){if(c=='-') neg=-1;c=getchar();}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
x*=neg;
}
const int MAXN=2500;
const int INF=0x3f3f3f3f;
int n,a[MAXN+5][MAXN+5],hd[MAXN+5],to[MAXN*2+5],nxt[MAXN*2+5],ec=0;
void adde(int u,int v){to[++ec]=v;nxt[ec]=hd[u];hd[u]=ec;}
int mn[MAXN+5],fa[MAXN+5];bool vis[MAXN+5];
void prim(){
vis[1]=1;for(int i=2;i<=n;i++) mn[i]=a[1][i],fa[i]=1;
for(int i=1;i<=n-1;i++){
int mnv=INF,k=0;
for(int j=1;j<=n;j++) if(!vis[j]&&mn[j]<mnv) mnv=mn[j],k=j;
vis[k]=1;
for(int j=1;j<=n;j++) if(!vis[j]&&mn[j]>a[k][j]) mn[j]=a[k][j],fa[j]=k;
}
}
void dfs(int x,int rt,int f,int mx){
if(f&&a[x][rt]>mx){printf("NOT MAGIC\n");exit(0);}
for(int e=hd[x];e;e=nxt[e]){
int y=to[e];if(y==f) continue;
dfs(y,rt,x,max(mx,a[x][y]));
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++) if(a[i][i]) return printf("NOT MAGIC\n"),0;
for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(a[i][j]!=a[j][i])
return printf("NOT MAGIC\n"),0;
prim();for(int i=2;i<=n;i++) adde(i,fa[i]),adde(fa[i],i);
for(int i=1;i<=n;i++) dfs(i,i,0,-1);printf("MAGIC\n");
return 0;
}
(午安,Kruskal 人)
Codeforces 632F - Magic Matrix(暴力 bitset or Prim 求最小生成树+最小瓶颈路)的更多相关文章
- Codeforces 632F Magic Matrix(bitset)
题目链接 Magic Matrix 考虑第三个条件,如果不符合的话说明$a[i][k] < a[i][j]$ 或 $a[j][k] < a[i][j]$ 于是我们把所有的$(a[i][j ...
- codeforces 632F. Magic Matrix (最小生成树)
You're given a matrix A of size n × n. Let's call the matrix with nonnegative elements magic if it i ...
- codeforces 632F. Magic Matrix
题目链接 给一个n*n的矩阵, 问是否对角线上的元素全都为0, a[i][j]是否等于a[j][i], a[i][j]是否小于等于max(a[i][k], a[j][k]), k为任意值. 前两个都好 ...
- Codeforces 878D - Magic Breeding(bitset,思维题)
题面传送门 很容易发现一件事情,那就是数组的每一位都是独立的,但由于这题数组长度 \(n\) 很大,我们不能每次修改都枚举每一位更新其对答案的贡献,这样复杂度必炸无疑.但是这题有个显然的突破口,那就是 ...
- POJ 1258 Agri-Net(Prim求最小生成树)
Agri-Net Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 64912 Accepted: 26854 Descri ...
- poj 2253 Frogger 最小瓶颈路(变形的最小生成树 prim算法解决(需要很好的理解prim))
传送门: http://poj.org/problem?id=2253 Frogger Time Limit: 1000MS Memory Limit: 65536K Total Submissi ...
- prim求最小生成树
一直以来只会Kruskal prim和dijkstra很像 只不过prim维护的是最短的边,而dijkstra维护的是最短的从起点到一个点的路径 同时prim要注意当前拓展的边是没有拓展过的 可以用堆 ...
- HDU 3371 kruscal/prim求最小生成树 Connect the Cities 大坑大坑
这个时间短 700多s #include<stdio.h> #include<string.h> #include<iostream> #include<al ...
- 新疆大学(新大)OJ xju 1009: 一带一路 prim求最短路径+O(n)素数筛选
1009: 一带一路 时间限制: 1 Sec 内存限制: 128 MB 题目描述 一带一路是去去年习大大提出来的建设“新丝绸之路经济带”和“21世纪海上丝绸之路”的战略构想.其中就包括我们新疆乌鲁木 ...
随机推荐
- Java版人脸检测详解上篇:运行环境的Docker镜像(CentOS+JDK+OpenCV)
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- JVM:类加载与字节码技术-2
JVM:类加载与字节码技术-2 说明:这是看了 bilibili 上 黑马程序员 的课程 JVM完整教程 后做的笔记 内容 这部分内容在上一篇笔记中: 类文件结构 字节码指令 编译期处理 类加载阶段 ...
- UltraSoft - Alpha - Scrum Meeting 2
Date: Apr 09th, 2020. 会议内容为完成初步的任务分工. Scrum 情况汇报 进度情况 组员 负责 昨日进度 后两日任务 CookieLau PM.后端 继续Django tuto ...
- 软件工程个人博客作业-软件案例分析:VS与VS Code
项目 内容 本作业属于北航 2020 年春软件工程 博客园班级连接 本作业是本课程个人项目作业 作业要求 我在这个课程的目标是 提高软件开发能力.团队协作能力 这个作业在哪个具体方面帮助我实现目标 提 ...
- Seata的一些概念
Seata的一些概念 一.什么是seata 二.AT模式的介绍 1.前提条件 2.整体机制 3.读写隔离的实现 1.写隔离 2.读隔离 三.事务分组 1.事务分组是什么? 2.通过事务分组如何找到后端 ...
- elasticsearch父子文档处理(join)
elasticsearch父子文档处理 join 一.背景 二.需求 三.前置知识 四.实现步骤 1.创建 mapping 2.添加父文档数据 3.添加子文档 4.查询文档 1.根据父文档id查询它下 ...
- Vue:Vue的介绍以及组件剖析
介绍 现在,随着基于JavaScript的单页应用程序(SPA)和服务器端渲染(SSR)的兴起,可以用JavaScript编写整个前端应用程序,并整洁地管理和维护该应用程序的前端代码.诸如Angula ...
- OpenWrt编译报错:Package airfly_receiver is missing dependencies for the following libraries
今天在编译一个OpenWrt测试用例的时候出现报错 Package airfly_receiver is missing dependencies for the following librarie ...
- 浅谈springboot自动配置原理
前言 springboot自动配置关键在于@SpringBootApplication注解,启动类之所以作为项目启动的入口,也是因为该注解,下面浅谈下这个注解的作用和实现原理 @SpringBootA ...
- ES6-字符串-模板字符串(复习+学习)
昨天学习了字符串对象和字符串的表示,就是利用utf-8等不同的编码方式,还有许多的对象方法,都是处理字符串的方法,挺方便的,今天我学习了一下字符串模板,这里记录i一下学习的笔记,当然,今天学习了部分内 ...