题面传送门

开始挖老祖宗(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. spark 解决错误java.io.InvalidClassException

    今天遇到一个现场问题,任务报错java.io.InvalidClassException.在开发环境是没有报错的,正式环境报错.大概类似于下面这样(非报错原文,摘自网上同类博客) java.io.In ...

  2. 【UE4 C++】 解析与构建 Json 数据

    准备条件 Json 格式 { "Players":[ { "Name": "Player1", "health": 20 ...

  3. JBOSS未授权访问漏洞利用

    1. 环境搭建 https://www.cnblogs.com/chengNo1/p/14297387.html 搭建好vulhub平台后 进入对应漏洞目录 cd vulhub/jboss/CVE-2 ...

  4. VS Code C/C++开发环境配置

    VS Code C/C++开发环境配置 一.安装 ​ 1.前往官网下载安装即可 https://code.visualstudio.com/ ​ 2.进入VS Code安装如下插件 二.C/C++开发 ...

  5. 字符串与模式匹配算法(六):Needleman–Wunsch算法

    一.Needleman-Wunsch 算法 尼德曼-翁施算法(英语:Needleman-Wunsch Algorithm)是基于生物信息学的知识来匹配蛋白序列或者DNA序列的算法.这是将动态算法应用于 ...

  6. SpringBoot:Spring容器的启动过程

    一.简述 Spring的启动过程就是IoC容器的启动过程,本质上就是创建和初始化Bean的工厂(BeanFactory),BeanFactory是整个SpringIoC的核心,Spring使用Bean ...

  7. amba web

    arm amba doc https://developer.arm.com/docs

  8. PCIE笔记--PCIe错误定义与分类

    转载地址:http://blog.chinaaet.com/justlxy/p/5100057782 前面的文章提到过,PCI总线中定义两个边带信号(PERR#和SERR#)来处理总线错误.其中PER ...

  9. Vue脚手架最新版本安装使用

    现在很多的插件如Vant 这类的样式框架,都去兼容了Vue的3.0版本,所以我总结一下如何去简单的搭建一个Vue3.0的框架 开始 一,如何安装 在这里说明一下,Vue脚手架版本,和Vue版本是两个东 ...

  10. 双链路接入(双出口)isp运营商(负载分担)

    USG作为校园或大型企业出口网关可以实现内网用户通过两个运营商访问Internet,并保护内网不受网络攻击. 组网需求 某学校网络通过USG连接到Internet,校内组网情况如下: 校内用户主要分布 ...