P2783 有机化学之神偶尔也会作弊 题解
前言
这道题以前还是道(水)黑题,现在怎么降紫了????
前置芝士
tarjain 缩点
倍增求LCA或树剖求LCA
脑子。。。
题意
给你一个无向图,要求你把所有的环缩成一个点。在新得到的图上问你两个点之间有多少个点。
分析
首先我们会由 "所有的环状碳都变成了一个碳" 想到要缩点。
但是无向图怎么缩点呢?
我们可以按照原来无向图那样缩点,但要注意的一点是 $to != fa[x] $
因为这是无向图,可能有的边会直接连向他父亲,假如我们要走这条边的话,就会重
复搜,就这样一直无限循环下去。剩下的就和有向图的缩点没什么区别了。
接着我们就要考虑每个询问。
我们把所有的环去掉后,就会得到一个有向无环图(树)。不理解的童鞋请画图自证
那么问题就会转化为树上问题。
甩给你一张图

假如我们要求 \(4\) 和 \(7\) 之间的有多少个点。我么可以用
\(dep[4] + dep[7] - 2 * dep[3]\) + 1
即 \(dep[x] + dep[y] - 2 * dep[lca(x,y)] + 1\)
由于他的深度有类似于前缀和的性质,所以我们可以这么处理。
为什么要减一呢? 因为你 $LCA $处 只能算一个点,但你却减了两次,所以要
重新加上
补充
关于一个数转二进制的方法。
我们可以联想到快速幂中要依次取出指数的二进制每一位,所以我们可以像快速幂
中的写法模拟出二进制每一位。
代码如下
void shuchu(int x)
{
int xx = 0;//记录有多少位
for(; x; x>>=1)//依次取出每一位上的数字
{
xx++;
if(x & 1) t[xx] = 1;
else t[xx] = 0;
}
for (int i = xx; i >= 1; i-- ) printf("%d",t[i]);//倒序输出
printf("\n");
}
几个要注意的点
求两个点的LCA 一定要在新建的图上求 (本蒟蒻就在这里卡了好几回)
树剖求 LCA 时要注意是在缩完点之后的图上求
tarjain 缩点时要注意不能访问到他父亲的边
不懂得同鞋 , 请看下面代码 ,下面有注释。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 1e5+10;
int n,m,u,v,x,y,tot,sum,cnt,num,topp,T;
int dep[N],fa[N],size[N],top[N],head[N],hed[N];
int shu[N],dfn[N],low[N],sta[N],son[N];
int t[N];
bool vis[N];
inline int read()//标准快读
{
int s = 0,w = 1; char ch = getchar();
while(ch < '0' || ch > '9'){if(ch == '-') w = -1; ch = getchar();}
while(ch >= '0' && ch <= '9'){s = s * 10+ch -'0'; ch = getchar();}
return s * w;
}
struct node{int to,net;}e[N<<1],edge[N<<1];//为了压行不择手段
void add(int x,int y)
{
e[++tot].to = y;
e[tot].net = head[x];
head[x] = tot;
}
void add_(int x,int y)//建新图上的边
{
edge[++sum].to = y;
edge[sum].net = hed[x];
hed[x] = sum;
}
void tarjain(int x,int fa)//缩点
{
dfn[x] = low[x] = num++;
sta[++topp] = x; vis[x] = 1;
for(int i = head[x]; i; i = e[i].net)
{
int to = e[i].to;
if(to == fa) continue;//特判是不是联向他父亲得边
if(!dfn[to])
{
tarjain(to,x);
low[x] = min(low[x],low[to]);
}
else if(vis[to])
{
low[x] = min(low[x],dfn[to]);
}
}
if(dfn[x] == low[x])//求强联通分量
{
cnt++; int y;
do
{
y = sta[topp--];
//size[cnt]++;
shu[y] = cnt;
vis[y] = 0;
}while(x != y);
}
}
void get_tree(int x)//树剖第一遍DFS求重儿子
{
dep[x] = dep[fa[x]] + 1; size[x] = 1;
for(int i = hed[x]; i; i = edge[i].net)
{
int to = edge[i].to;
if(to == fa[x]) continue;
fa[to] = x;
get_tree(to);
size[x] += size[to];
if(size[son[x]] > size[to]) son[x] = to;
}
}
void dfs(int x,int topp)//树剖第二遍DFS求每条链的顶端
{
top[x] = topp;
if(son[x]) dfs(son[x],topp);
for(int i = hed[x]; i; i = edge[i].net)
{
int to = edge[i].to;
if(to == fa[x] || to == son[x]) continue;
dfs(to,to);
}
}
int lca(int x,int y)//树剖求LCA
{
while(top[x] != top[y])
{
if(dep[top[x]] < dep[top[y]]) swap(x,y);
x = fa[top[x]];
}
if(dep[x] < dep[y]) return x;
else return y;
}
void shuchu(int x)//二进制转化
{
int xx = 0;
for(; x; x>>=1)
{
xx++;
if(x & 1) t[xx] = 1;
else t[xx] = 0;
}
for (int i = xx; i >= 1; i--) printf("%d",t[i]);
printf("\n");
}
int main()
{
n = read(); m = read();
for(int i = 1; i <= m; i++)
{
u = read(); v = read();
add(u,v); add(v,u);
}
for(int i = 1; i <= n; i++)
{
if(!dfn[i]) tarjain(i,0);
}
for(int i = 1; i <= n; i++)//缩点
{
for(int j = head[i]; j; j = e[j].net)
{
int to = e[j].to;
if(shu[to] != shu[i])
{
add_(shu[i],shu[to]);
}
}
}
get_tree(1);
dfs(1,1);
T = read();
while(T--)
{
x = read(); y = read();
int Lca = lca(shu[x],shu[y]);
int ans = dep[shu[x]] + dep[shu[y]] - 2 * dep[Lca] + 1;//计算每个询问的答案
shuchu(ans);
}
return 0;
}
P2783 有机化学之神偶尔也会作弊 题解的更多相关文章
- 洛谷 P2783 有机化学之神偶尔会做作弊 解题报告
P2783 有机化学之神偶尔会做作弊 题目背景 XS中学化学竞赛组教练是一个酷爱炉石的人. 有一天他一边搓炉石一边监考,而你作为一个信息竞赛的大神也来凑热闹. 然而你的化竞基友却向你求助了. &quo ...
- 【题解】Luogu P2783 有机化学之神偶尔会做作弊
原题链接:P2783 有机化学之神偶尔会做作弊 一看,是黑题,太毒瘤了,不写 什么单链??! 只会画有机化学中正六边形的我觉得这样不行QAQ(我才初二) 当然,题目也给你了详细的解释 实际呢,这道题先 ...
- luogu P2783 有机化学之神偶尔会做作弊 |Tarjan+LCA
题目背景 XS中学化学竞赛组教练是一个酷爱炉石的人. 有一天他一边搓炉石一边监考,而你作为一个信息竞赛的大神也来凑热闹. 然而你的化竞基友却向你求助了. "第1354题怎么做"&l ...
- 洛谷 P2783 有机化学之神偶尔会做作弊(Tarjan,LCA)
题目背景 LS中学化学竞赛组教练是一个酷爱炉石的人. 有一天他一边搓炉石一边监考,而你作为一个信息竞赛的大神也来凑热闹. 然而你的化竞基友却向你求助了. “第1354题怎么做”<--手语 他问道 ...
- LuoGu P2783 有机化学之神偶尔会做作弊
题目传送门 人生第一道黑题呢,虽然这题是黑题中的水题并且我调了一整节课,但是我还是很兴奋啊.毕竟人生第一道黑题啊 这个题根据题意,先把整个图进行tarjan缩点,建出一棵树,对于每一组询问,两点之间的 ...
- [洛谷P2783]有机化学之神偶尔会做作弊
第一次做出来黑题祭 虽然感觉难度其实并不到黑题的难度 题解: 其实这道题并没用什么特别的知识,只是Tarjan求双联通分量和LCA的结合. 所以,我们可以很显然的发现(如此恶劣的词汇,逃 这道题其实就 ...
- Tarjan+LCA【洛谷P2783】 有机化学之神偶尔会做作弊
[洛谷P2783] 有机化学之神偶尔会做作弊 题目背景 XS中学化学竞赛组教练是一个酷爱炉石的人. 有一天他一边搓炉石一边监考,而你作为一个信息竞赛的大神也来凑热闹. 然而你的化竞基友却向你求助了. ...
- 【洛谷 P2783】 有机化学之神偶尔会做作弊 (双联通分量)
题目链接 可能是除了<概率论>的最水的黑题了吧 用\(Tarjan\)缩点(点双联通分量),然后就是树上两点之间的距离了,跑\(LCA\)就好了. #include <cstdio& ...
- 洛谷P2783 有机化学之神偶尔会作弊
题目传送门 啦啦啦,发个文纪念一下第一道在洛谷上A的黑题,一次性就过真是无比舒服-(虽然某些大佬说这题有点水……)题目其实思路不难,Tarjan缩点+LCA,不过因为是无向边,所以在Tarjan的时候 ...
随机推荐
- python给excel文件加密码,并重新生成文件
需安装pywin32 pip install pywin32 直接上源码.简单几行就搞定 很多人学习python,不知道从何学起.很多人学习python,掌握了基本语法过后,不知道在哪里寻找案例上手. ...
- oeasy教您玩转linux010106这儿都有啥 ls
回忆上次内容 上个实验我们查询了 ls 的手册: man ls
- Fitness - 07.07
倒计时177天 运动53分钟,共计8组半,5.5公里.拉伸5分钟. 每组跑步5分钟(6.5KM/h),走路1分钟(5.5KM/h). 感冒+姨妈耽搁了大半月的时间 终于进入第六周的训练了~~!加油~! ...
- Unity中接收服务器消息并广播事件的实现
最近接触的项目,是一个棋牌游戏,棋牌游戏需要在前端进行一些操作,然后向服务器发送数据包,在服务器接收到客户端的数据后,会在服务端进行一系列的判断之后,然后发送给客户端一个返回数据,客户端接收到这个返回 ...
- 前端通过jqplot绘制折线图
首先需要下载jqplot需要的js与css文件,我已近打包好了,需要的可以下载 接下来导入其中关键的js与css如下, <link href="css/jquery.jqplot.mi ...
- Google Code Jam 2020 Round1B Join the Ranks
题意 给你一个形如\(1,2,\cdots,R,1,2,\cdots,R,1\cdots\)的序列,共重复\(C\)次.你每次可以选择一个区间\([L,R]\)将其平移到序列首部,最终使得序列具有\( ...
- C++11中一个使用for+auto时容易发生的bug
C++11中一个使用for+auto时容易发生的bug 一个小坑,那就是忘记在for循环中使用auto时加引用. 例如: for(auto num : nums){ // do some thing ...
- basicInterpreter1.02 增加对for循环的支持
源码下载:https://files.cnblogs.com/files/heyang78/basicInterpreter102-20200531-2.rar 输入: for x= to print ...
- canvas绘制图片drawImage学习
不得不说,html5中的canvas真的非常强大,从图片处理,到视频处理,再到游戏开发,都能见到canvas的身影,然而,就这一个<canvas>标签,功能居然如此强大,这主要归功于can ...
- Win10 在VM里面装Centos7.4后使用桥接模式连接外网,并用MobaXterm远程虚拟机详细教程
1.首先登陆虚拟机如图 2.执行ping命令看虚拟机里面的centos7是否可以连网 ping www.baiducom 此时会报错:name or service not known 3.设置一个文 ...