题目链接

Luogu

Codeforces

题意简述

某公司中有 \(n\) 名员工。为方便起见,将这些员工从 1 至 \(n\) 编号。起初,员工之间相互独立。接下来,会有以下 \(m\) 次操作:

  1. 员工 \(y\) 成为员工 \(x\) 的上司。保证此前 \(x\) 没有上司

  2. 员工 \(x\) 拿到一份文件并签字,随后交给他的上司。他的上司签字后,再交给更上一级。依此类推,直到文件传递到的那个人没有上司为止。

  3. 询问员工 \(x\) 是否在第 \(i\) 件文件上签过字。文件编号为上一件文件的编号再加 1,第一件文件的编号为 1。如果是,输出 YES,否则输出 NO

解法说明

显然,我们可以将员工之间的关系看作森林,将每个员工看作一个节点,其与上司的关系看作一条边。之所以不是一棵树,是因为在 \(m\) 次操作中,有些人可能并没有被指定上司,所以员工之间的关系很可能并不是一棵树而是森林

通过观察题面可以发现,一个员工在成为另一个员工的上司后,就不会再有更改了。由于在线操作过于麻烦,我们可以考虑离线

具体离线方法如下:

  • 对于操作 1,直接连边即可,不过这里还要在线维护一个并查集

  • 对于操作 2,分别记下第一个和最后一个对文件签字的员工,后者就是前者所在的连通块的根,利用并查集查找;

  • 对于操作 3,分别记下员工编号及文件编号,离线回答

接下来分析如何回答询问。可以发现,如果询问的员工 \(x\) 在 最开始看到文件 \(i\) 的员工与最后看到文件 \(i\) 的员工之间的链上,那么 \(x\) 就看过文件。所以,问题就被转化为了判断 \(x\) 是否在这条链上

考虑如何判断。

设 \(st\) 为链的起始点,\(ed\) 为截止点,可推得如 \(x\) 在链上,则 \(\text{lca}(x,st) = x\) 且 \(\text{lca}(x,ed) = ed\),维护一个 LCA 即可求解。我这里用的是树剖求 LCA,倍增也可以。

还有一些细节需要注意。由于员工之间的关系是森林而非一棵树,所以我们在预处理树剖时应枚举每个点,如果该点是其所属的连通块的根,就对其进行一次预处理,且回答询问时应首先判断 \(x\) 与 \(st\)、\(ed\) 是否在同一连通块内,如果不在直接输出 NO,否则再执行下一步操作。

剩余细节详见下面代码中的注释。

通过代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int,int>
#define mp make_pair
const int N=1e5+10; namespace IO{
//快读
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){
if(ch=='-'){
f=-1;
}
ch=getchar();
}
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);
ch=getchar();
}
return x*f;
} //快写
inline void write(int x){
if(x<0){
putchar('-');
x=-x;
}
if(x>9){
write(x/10);
}
putchar(x%10+'0');
}
} using namespace IO; namespace code{
//链式前向星存图
int head[N],tot; struct node{
int ver,next;
}t[N<<1]; void add(int x,int y){
t[++tot].ver=y,t[tot].next=head[x],head[x]=tot;
} //并查集
int fa[N]; int getfa(int x){
if(fa[x]==x){
return x;
}
return fa[x]=getfa(fa[x]);
} //树链剖分
int fat[N],size[N],son[N],deep[N],top[N]; void dfs1(int x){
size[x]=1;
int maxson=-1;
for(int i=head[x];i;i=t[i].next){
int y=t[i].ver;
if(y==fat[x]){
continue;
}
fat[y]=x;
deep[y]=deep[x]+1;
dfs1(y);
if(size[y]>maxson){
maxson=size[y];
son[x]=y;
}
size[x]+=size[y];
}
} void dfs2(int x,int from){
top[x]=from;
if(!son[x]){
return;
}
dfs2(son[x],from);
for(int i=head[x];i;i=t[i].next){
int y=t[i].ver;
if(y==son[x]||y==fat[x]){
continue;
}
dfs2(y,y);
}
} //求LCA
int lca(int x,int y){
while(top[x]!=top[y]){
if(deep[top[x]]<deep[top[y]]){
swap(x,y);
}
x=fat[top[x]];
}
if(deep[x]<deep[y]){
return x;
}
return y;
} //主程序
int n,m,f_tot,q_tot;
PII file[N],query[N]; void solve(){
n=read(),m=read();
for(int i=1;i<=n;i++){//并查集预处理
fa[i]=i;
}
for(int i=1;i<=m;i++){//离线处理
int op=read();
if(op==1){
int x=read(),y=read();
add(y,x);//单向边
fa[x]=getfa(y);//在线维护并查集
}else if(op==2){
int x=read();
file[++f_tot]=mp(x,getfa(x));
}else{
int x=read(),y=read();
query[++q_tot]=mp(x,y);
}
}
for(int i=1;i<=n;i++){//枚举所有点
if(getfa(i)==i){//判断是否为所在连通块的根
deep[i]=1;//树剖预处理
fat[i]=i;
dfs1(i);
dfs2(i,i);
}
}
for(int i=1;i<=q_tot;i++){
int x=query[i].first,y=query[i].second,st=file[y].first,ed=file[y].second;
if(getfa(x)!=getfa(st)){//是否在同一个连通块
printf("NO\n");
continue;
}
if(lca(x,st)==x&&lca(x,ed)==ed){//判断
printf("YES\n");
}else{
printf("NO\n");
}
}
}
} using namespace code; signed main(){
solve();
return 0;
}

CF466E Information Graph 题解的更多相关文章

  1. Codeforces 466 E. Information Graph

    并查集.... E. Information Graph time limit per test 1 second memory limit per test 512 megabytes input ...

  2. Codeforces 466E Information Graph

    Information Graph 把询问离线之后就能随便搞了, 去check一下是不是祖先, 可以用倍增也能用dfs序. #include<bits/stdc++.h> #define ...

  3. POJ 1737 Connected Graph 题解(未完成)

    Connected Graph Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 3156   Accepted: 1533 D ...

  4. URAL2127 Determinant of a Graph 题解

    这个题真的折磨了我超久的.全网几乎搜不到一个详细的题解,俺来写写吧. 题意:给你一个无自环无重边的连通无向图,求它邻接矩阵的行列式的值. \(n\le 2*10^5,n-1\le m \le n+50 ...

  5. CodeForces 466E Information Graph --树形转线性+并查集

    题意:有三种操作: 1.新增一条边从y连向x,此前x没有父节点 2.x接到一份文件,(文件标号逐次递增),然后将这份文件一路上溯,让所有上溯的节点都接到这份文件 3.查询某个节点x是否接到过文件F 解 ...

  6. 466E - Information Graph 巧妙的判断祖先于孩子的关系

    这题说的是给了一个公司员工100000 然后现在又3种操作第一种将y置为x的父亲,第二种操作将文件给第x个人签他签完给他的上司签,一直到没有上司为止,第三种操作问x是否签了第i份文件,然后 我们只要直 ...

  7. CF1581B Diameter of Graph 题解

    Content \(\textsf{CQXYM}\) 想要构造一个包含 \(n\) 个点和 \(m\) 条边的无向连通图,并且他希望这个图满足下列条件: 该图中不存在重边和自环.也就是说,一条边应该连 ...

  8. [Leetcode Week3]Clone Graph

    Clone Graph题解 原创文章,拒绝转载 题目来源:https://leetcode.com/problems/clone-graph/description/ Description Clon ...

  9. 【Lintcode】137.Clone Graph

    题目: Clone an undirected graph. Each node in the graph contains a label and a list of its neighbors. ...

  10. CodeForces 715B Complete The Graph 特殊的dijkstra

    Complete The Graph 题解: 比较特殊的dij的题目. dis[x][y] 代表的是用了x条特殊边, y点的距离是多少. 然后我们通过dij更新dis数组. 然后在跑的时候,把特殊边都 ...

随机推荐

  1. Dapper升级SqlSugar问题汇总

    最近群里有个小伙伴把Dapper迁移SqlSugar几个不能解决的问题进行一个汇总,我正好写一篇文章来讲解一下 一.sql where in传参问题: SELECT * FROM users wher ...

  2. Django——启动项目时报错mysqlclient

    报错内容如下: Watching for file changes with StatReloader Exception in thread django-main-thread: Tracebac ...

  3. AI实战 | 使用元器打造浪漫仪式小管家

    浪漫仪式小管家 以前我们曾经打造过学习助手和待办助手,但这一次,我们决定创造一个与众不同的智能体,而浪漫将成为我们的主题.我们选择浪漫作为主题,是因为我们感到在之前的打造过程中缺乏了一些仪式感,无法给 ...

  4. openCV编译安装-MSCV-Windows10-Qt

    openCV编译安装-MSCV-Windows10-Qt 1.准备工作 CMake:下载最新版本即可 openCV:下载任意版本,可以下载源码或者官方编译好的VS版(其中也带有源码),我下载的是ope ...

  5. vue3+vant 引入Dialog Toast都会失败报错not defined

    今天在封装vant组件的时候,刚好要用到toast提示信息的组件,索性就按照官网提供的引入方法进行正常的引入,嘿,好家伙,一顿操作下来后发现竟然报Toast未定义,这就纳闷了,明明步骤都是对的啊,所以 ...

  6. ko编译常见问题

    记录ko编译过程中遇到的常见问题: (1)找不到标准库头文件 解决方式:将lib库中的头文件链接到编译内核中.

  7. Kafka--Rebalance重平衡

    Rebalance总览 Rebalance触发条件 (1)消费组成员发生变更,有新消费者加入或者离开,或者有消费者崩溃 (2)消费者组订阅的主题数量发生变更 (3)消费组订阅主题的分区数发生变更 避免 ...

  8. 如何监控文件变化,比如密码修改导致 shadow 文件变化

    原始需求是如果系统的密码被修改,或者创建了新用户,就告警出来.本质上,只需要监控 /etc/shadow 文件变化即可.但是在指标监控体系里,这个事情就比较棘手,只能把文件的 mtime 作为指标的值 ...

  9. JavaScript执行模型笔记

    JavaScript执行模型 引言 JavaScript是一个单线程(Single-threaded)异步(Asynchronous)非阻塞(Non-blocking)并发(Concurrent)语言 ...

  10. Mysql中innodb的B+tree能存储多少数据?

    引言 InnoDB一棵3层B+树可以存放多少行数据?这个问题的简单回答是:约2千万.为什么是这么多呢?因为这是可以算出来的,要搞清楚这个问题,我们先从InnoDB索引数据结构.数据组织方式说起. 在计 ...