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世纪海上丝绸之路”的战略构想.其中就包括我们新疆乌鲁木 ...
随机推荐
- SpringCloud 2020.0.4 系列之Eureka
1. 概述 老话说的好:遇见困难,首先要做的是积极的想解决办法,而不是先去泄气.抱怨或生气. 言归正传,微服务是当今非常流行的一种架构方式,其中 SpringCloud 是我们常用的一种微服务框架. ...
- 从浏览器发送请求给SpringBoot后端时,是如何准确找到哪个接口的?(下篇)
纸上得来终觉浅,绝知此事要躬行 注意: 本文 SpringBoot 版本为 2.5.2; JDK 版本 为 jdk 11. 前言: 前文:你了解SpringBoot启动时API相关信息是用什么数据结构 ...
- JVM:GC Roots
JVM:GC Roots 本笔记是根据bilibili上 尚硅谷 的课程 Java大厂面试题第二季 而做的笔记 JVM 垃圾回收的时候如何确定垃圾 什么是垃圾 简单来说就是内存中已经不再被使用的空间就 ...
- 【做题记录】CF1451E2 Bitwise Queries (Hard Version)
CF1451E2 Bitwise Queries (Hard Version) 题意: 有 \(n\) 个数( \(n\le 2^{16}\) ,且为 \(2\) 的整数次幂,且每一个数都属于区间 \ ...
- spring-cloud-square开发实战(三种类型全覆盖)
欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码):https://github.com/zq2599/blog_demos 本篇概览 前文<五分钟搞懂spring-clou ...
- VIVADO 2017.4配置MIG IP注意事项
1.2GB的single rank SODIMMs配置pin还是和以前一样没有问题: 2.8GB SODIMMs配置pin需要注意4点: (1).所有的DDR3引脚都需要在连续的BANK上,例如Z71 ...
- 从0到1搭建自己的组件(vue-code-view)库(下)
0x00 前言 书接上文,本文将从源码功能方面讲解下 vue-code-view 组件核心逻辑,您可以了解以下内容: 动态组件的使用. codeMirror插件的使用. 单文件组件(SFC,singl ...
- linux下测试读写
1.测/目录所在磁盘的纯写速度: time dd if=/dev/zero bs=1024 count=1000000 of=/1Gb.file 2.测/目录所在磁盘的纯读速度: time dd if ...
- mysql数据库导入导出文件sql文件
window下 1.导出整个数据库 mysqldump -u 用户名 -p 数据库名 > 导出的文件名 mysqldump -u dbuser -p dbname > dbname.sql ...
- VulnHub-[DC-8-9]-系列通关手册
DC8-通关手册 DC-8是另一个专门构建的易受攻击的实验室,目的是在渗透测试领域积累经验. 这个挑战有点复杂,既是实际挑战,又是关于在Linux上安装和配置的两因素身份验证是否可以阻止Linux服务 ...