首页 > 其他分享 >CF466E Information Graph 题解

CF466E Information Graph 题解

时间:2024-07-19 21:41:19浏览次数:9  
标签:CF466E Information ch 上司 文件 int 题解 员工 编号

题目链接

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,ch,上司,文件,int,题解,员工,编号
From: https://www.cnblogs.com/Alexxtl/p/18312437

相关文章

  • P3206 城市建设 题解
    Statement动态边权的最小生成树。\(n\le2\cdot10^4,m,q\le5\cdot10^4\)Solution法一:改边权相当于删一条边再加一条边.考虑LCT维护最小生成树,加边好做,但删边比较麻烦,于是采用线段树分治,撤销一条边是好做的,cut再link一下就行了.\(O(n\logn\logq)\),常数较大.法二:两个结......
  • 07-19 题解
    07-19题解B思路实质:有一个完全图,删掉一些边,然后在图上找一棵生成树但是图的边数是\(n^2\)级别的,极其稠密找生成树的步骤:从一个点开始,把与它相连的,不在同一连通块的点连在一起所以我们只要确保每次都能在尽量少的步数内找到一个合法点一共有n个点,确定一个点之......
  • 2024年牛客暑期多校训练营1 A题 A Bit Common题解
    题目的大意:首先,给你一个长度为n的序列A,A序列中每一个元素全都小于2m,并且大于等于0。A序列要满足存在一个非空子序列的与运算(&)和为1;输出这样的A序列有几个,最后对正整数q取模。(1<=n,m<=5000,1<=q<=109)输入只有一行n,m,q,输出包含一个整数。 题解:要满......
  • INFS5710 Information Business Analytics
    INFS5710 InformationTechnology Infrastructurefor BusinessAnalyticsProjectStatement(Due by noon 12 PM on Monday29July2024via Moodle)• This project accounts for 25% of the total marks for this course.• Thedeliverablesarea......
  • [NOI2007] 项链工厂 题解
    前言题目链接:洛谷;Hydro&bzoj。题意简述yzh喜欢写DS题!你要维护一个环:顺时针移动\(k\)位;翻转\(2\simn\);交换\(i\)与\(j\);区间覆盖;查询整个环有几个颜色段;查询\(i\simj\)有几个颜色段。题目分析平衡树板子啊,代码很好写,\(273\)行。但是为什么不使用线......
  • 1004:字符三角形 题解
    题目链接题目描述给定一个字符,用它构造一个底边长\(5\)个字符,高\(3\)个字符的等腰字符三角形。解题思路由于是字符,我们需要定义一个char类型的字符变量。第一行为两个空格和一个字符第二行为一个空格和三个字符第三行是五个字符输出即可ACCode#include<bits/stdc++.h......
  • 1003:对齐输出 题解
    题目链接题目描述读入三个整数,按每个整数占\(8\)个字符的宽度,右对齐输出它们,按照格式要求依次输出三个整数,之间以一个空格分开。解题思路由于我们不知道这个数有多大,所以我们可以用printf自带的占位符%xd输出,其中x为位数。例:printf("%3d",a);就是占用3位。题目要求为\(8\)位......
  • 1001:Hello,World! 题解
    题目链接题目描述编写一个能够输出“\(\mathtt{Hello,World!}\)”的程序,这个程序常常作为一个初学者接触一门新的编程语言所写的第一个程序,也经常用来测试开发、编译环境是否能够正常工作。提示:“\(\mathtt{Hello,World!}\)”中间没空格。解题思路梦开始的地方\(Ver3.0\)没......
  • 暑假集训CSP提高模拟1考试题解
    A.Start洛谷原题链接一道大模拟,赛时20pts。教授の高光时刻-输出没加句号、空格。-C++向0取整。-DOUBLE没传递。--9操作成-1(复制粘贴导致的)。-负数位运算卡常。其实这题还是比较简单的,细节在题目中讲的很详细,跟着它说的去做就好了。我的方法是把每个玩家用一个结构......
  • [AGC012E] Camel and Oases 题解
    题目链接题目链接题目解法可能并没有那么难(?首先\(V\)的取值只有\(\logV\)种,即\(\lfloor\frac{V}{2^k}\rfloor\)称\(\lfloor\frac{V}{2^k}\rfloor\)为第\(k\)层,先预处理出每一层的极大连通区间我们可以把问题抽象成:每一层中选一个区间,要求覆盖\([1,n]\),且第\(0......