题面传送门

开始挖老祖宗(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 求最小生成树+最小瓶颈路)的更多相关文章

  1. Codeforces 632F Magic Matrix(bitset)

    题目链接  Magic Matrix 考虑第三个条件,如果不符合的话说明$a[i][k] < a[i][j]$ 或 $a[j][k] < a[i][j]$ 于是我们把所有的$(a[i][j ...

  2. 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 ...

  3. codeforces 632F. Magic Matrix

    题目链接 给一个n*n的矩阵, 问是否对角线上的元素全都为0, a[i][j]是否等于a[j][i], a[i][j]是否小于等于max(a[i][k], a[j][k]), k为任意值. 前两个都好 ...

  4. Codeforces 878D - Magic Breeding(bitset,思维题)

    题面传送门 很容易发现一件事情,那就是数组的每一位都是独立的,但由于这题数组长度 \(n\) 很大,我们不能每次修改都枚举每一位更新其对答案的贡献,这样复杂度必炸无疑.但是这题有个显然的突破口,那就是 ...

  5. POJ 1258 Agri-Net(Prim求最小生成树)

    Agri-Net Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 64912   Accepted: 26854 Descri ...

  6. poj 2253 Frogger 最小瓶颈路(变形的最小生成树 prim算法解决(需要很好的理解prim))

    传送门: http://poj.org/problem?id=2253 Frogger Time Limit: 1000MS   Memory Limit: 65536K Total Submissi ...

  7. prim求最小生成树

    一直以来只会Kruskal prim和dijkstra很像 只不过prim维护的是最短的边,而dijkstra维护的是最短的从起点到一个点的路径 同时prim要注意当前拓展的边是没有拓展过的 可以用堆 ...

  8. HDU 3371 kruscal/prim求最小生成树 Connect the Cities 大坑大坑

    这个时间短 700多s #include<stdio.h> #include<string.h> #include<iostream> #include<al ...

  9. 新疆大学(新大)OJ xju 1009: 一带一路 prim求最短路径+O(n)素数筛选

    1009: 一带一路 时间限制: 1 Sec  内存限制: 128 MB 题目描述 一带一路是去去年习大大提出来的建设“新丝绸之路经济带”和“21世纪海上丝绸之路”的战略构想.其中就包括我们新疆乌鲁木 ...

随机推荐

  1. RabbitMQ:从入门到搞定面试官

    安装 使用docker安装,注意要安装tag后缀为management的镜像(包含web管理插件),我这里使用的是rabbitmq:3.8-management 1. 拉取镜像 shell docke ...

  2. 性能利器 Takin 来了!首个生产环境全链路压测平台正式开源

    6 月 25 日,国内知名的系统高可用专家数列科技宣布开源旗下核心产品能力,对外开放生产全链路压测平台产品的源代码,并正式命名为 Takin. 目前中国人寿.顺丰科技.希音.中通快递.中国移动.永辉超 ...

  3. JVM:内存结构

    JVM:内存结构 说明:这是看了 bilibili 上 黑马程序员 的课程 JVM完整教程 后做的笔记 内容 程序计数器 虚拟机栈 本地方法栈 堆 方法区 直接内存 1. 程序计数器 1.1 定义 P ...

  4. [对对子队]Beta设计和计划

    需求再分析 Alpha阶段用户反馈的问题主要有三个 新手引导部分没有明确指出合成按钮可以使用下拉框切换目标,因此不少玩家卡在第三关 觉得合成动画太长,希望可以快进或者跳过 对游戏目标很迷惑,不知道为什 ...

  5. 航胥:北航教务助手——Alpha阶段发布声明

    本软件项目为北京航空航天大学2020年"软件工程"课程项目 v1.0.5版本下载地址:https://bhpan.buaa.edu.cn:443/#/link/D10B71B69E ...

  6. (五)、Docker 容器数据卷

    1.什么是数据卷 将运用与运行的环境打包形成容器运行 ,运行可以伴随着容器,但是我们对数据的要求希望是持久化的 容器之间希望有可能共享数据 Docker容器产生的数据,如果不通过docker comm ...

  7. 极简实用的Asp.NetCore框架再新增商城模块

    概述 关于这个框架的背景,在前面我已经交代过了.不清楚的可以查看这个链接 1.极简实用的Asp.NetCore模块化框架决定免费开源了 2.极简实用的Asp.NetCore模块化框架新增CMS模块 算 ...

  8. 确定字符互异 牛客网 程序员面试金典 C++ Python

    确定字符互异 牛客网 程序员面试金典 C++ Python 题目描述 请实现一个算法,确定一个字符串的所有字符是否全都不同.这里我们要求不允许使用额外的存储结构. 给定一个string iniStri ...

  9. Codeforces Round #747 (Div. 2)题解

    谢天谢地,还好没掉分,还加了8分,(8分再小也是加啊)前期刚开始有点卡,不过在尽力的调整状态之后,还是顺利的将前面的水题过完了,剩下的E2和F题就过不去了,估计是能力问题,自己还是得认真补题啦. E2 ...

  10. hdu 5056 Boring count (类似单调队列的做法。。)

    给一个由小写字母构成的字符串S,问有多少个子串满足:在这个子串中每个字母的个数都不超过K. 数据范围: 1<=T<= 1001 <= the length of S <= 10 ...