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的时候 ...
随机推荐
- Database4.exe用来导入excel
从ACCESS数据库导出的EXCEL表格,可以通过database4.exe来连接,并导出sql脚本,再用database4.exe来连接ACCESS并先创建于脚本结构一致的表,然后复制脚本,从新生成 ...
- 知识点干货——CSS动画
CSS动画 (transition.animation) //2D动画 transform:translate(); /*偏移*/ transform:rotate(); /*旋转角度*/ trans ...
- Spring security OAuth2.0认证授权学习第三天(认证流程)
本来之前打算把第三天写基于Session认证授权的,但是后来视屏看完后感觉意义不大,而且内容简单,就不单独写成文章了; 简单说一下吧,就是通过Servlet的SessionApi 通过实现拦截器的前置 ...
- mysql InnoDB引擎是否支持hash索引
看一下mysql官方文档:https://dev.mysql.com/doc/refman/5.7/en/create-index.html , 从上面的图中可以得知,mysql 是支持hash索引的 ...
- Java代码工具EasyCode使用
写Java代码,增删改查,最无趣而又最基础.那机器人就来了,帮你写,减少你的基础的无趣的工作. 推荐两个代写代码的神奇工具Mybatis-generator与EasyCode.这两款软件的数据库持久层 ...
- shell进行微信报警的简单应用
一.企业微信注册地址: https://work.weixin.qq.com/?from=openApi二.请求格式 1.获取token的方法 curl -s 'https://qyapi.weixi ...
- Mybatis和Mybatis-Plus时间范围查询,亲测有效
一.mysql 1.传入时间范围参数类型是字符串 <if test="startTime!=null and startTime.trim() neq ''"> and ...
- python文件的相关操作
python 目录 python 1.python文件的介绍 使用文件的目的 Python文件的类型主要有两种:文本文件和二进制文件. 操作文件的流程主要有三步:打开-操作-关闭操作. 2.文件的打开 ...
- nginx中编写lua拦截器
先配置nginx location ~ .*\.(php|php5)?$ { if ($request_uri ~ "one/tes ...
- spring framework源码之AnnotationConfigApplicationContext
AnnotationConfigApplicationContext 内部使用了AnnotatedBeanDefinitionReader:ClassPathBeanDefinitionScanner ...