LGP5089题解
2024-09-08 07:31:44
考试的一道题,考场上sb了没写出来,然后在两天后的英语课上胡出来了(
首先猜一个奇怪的结论:
对于元素 \((a,b)\),看做连接第 \(a\) 列和第 \(b\) 行的一条边,那么如果一行和一列在同一个联通块内(第 \(x\) 行和第 \(y\) 列),那么 \((x,y)\) 处一定能通过核聚变生成,或本来就有一个元素。
证明:
考虑数学归纳法。
假设一行与一列之间一共有 \(2k-1\) 条边。
- \(k=1\)
很明显,该点有一个元素。
2. \(k=2\)
假设三条边为 \((a,b)(b,c)(c,d)\)。
看一眼核聚变的规则,发现 \((a,d)\) 有一条边。
3. \(k>2\)
两个点之间有边的条件是存在一条长度为 \(3\) 的路径连接这两个点。
而结论 \(k=2\) 的情况说明长度为 \(3\) 边能变成一条长度为 \(1\) 的边,也就是这两个节点之间的距离减少了 \(2\)。
然后我们发现 \(2k-1 - 2(k-1) = 1\) 即两点间有边,也就是这一行和这一列连接在了一起。
证毕。
这个结论告诉我们,我们只需要将所有行和列连接在一起就行了。
所有我们只需要数有多少个联通块,数量-1即为答案。
code:
#include<cstdio>
const int M=2e5+5;
int n,m,q;bool vis[M<<1];
struct Edge{
int to;Edge*nx;
}e[M<<1],*h[M<<1],*cnt=e;
inline void Add(const int&u,const int&v){
*cnt=(Edge){v,h[u]};h[u]=cnt++;
*cnt=(Edge){u,h[v]};h[v]=cnt++;
}
void DFS(int u){
vis[u]=true;
for(Edge*E=h[u];E;E=E->nx){
int v=E->to;
if(vis[v])continue;
DFS(v);
}
}
signed main(){
register int i,u,v,ans=0;
scanf("%d%d%d",&n,&m,&q);
for(i=1;i<=q;++i){
scanf("%d%d",&u,&v);
Add(u,v+n);
}
for(i=1;i<=n+m;++i){
if(!vis[i])DFS(i),++ans;
}
printf("%d",ans-1);
}
最新文章
- PhpStorm/Xdebug安装使用
- 总结JS 常用函数
- [翻译]:SQL死锁-死锁排除
- call与apply基础用法告诉你
- Markdown语法速查
- createjs 更新
- 正则表达式中参数g、i、m的作用(share)
- ISA中的WEB链
- 全排列算法(字典序法、SJT Algorithm 、Heap&#39;s Algorithm)
- c++11:iota
- sed 命令详解
- 00-Unit_Common综述-RecyclerView封装
- maven私服上传jar包
- 【默认加入持久化机制,防止消息丢失,v0.0.3】对RabbitMQ.Client进行一下小小的包装,绝对实用方便
- cvc-complex-type.2.4.a: Invalid content was found starting with element &#39;asy
- [转]Laravel - Where null and Where not null eloquent query example
- 编写blog第一天
- nodejs之glob与globby
- centos7如何添加开机启动服务/脚本
- java中常用的进制转换