Information Graph CodeForces - 466E

There are n employees working in company “X” (let’s number them from 1 to n for convenience). Initially the employees didn’t have any relationships among each other. On each of m next days one of the following events took place:

either employee y became the boss of employee x (at that, employee x didn’t have a boss before);
or employee x gets a packet of documents and signs them; then he gives the packet to his boss. The boss signs the documents and gives them to his boss and so on (the last person to sign the documents sends them to the archive);
or comes a request of type “determine whether employee x signs certain documents”.
Your task is to write a program that will, given the events, answer the queries of the described type. At that, it is guaranteed that throughout the whole working time the company didn’t have cyclic dependencies.

Input
The first line contains two integers n and m (1 ≤ n, m ≤ 105) — the number of employees and the number of events.

Each of the next m lines contains the description of one event (the events are given in the chronological order). The first number of the line determines the type of event t (1 ≤ t ≤ 3).

If t = 1, then next follow two integers x and y (1 ≤ x, y ≤ n) — numbers of the company employees. It is guaranteed that employee x doesn’t have the boss currently.
If t = 2, then next follow integer x (1 ≤ x ≤ n) — the number of the employee who got a document packet.
If t = 3, then next follow two integers x and i (1 ≤ x ≤ n; 1 ≤ i ≤ [number of packets that have already been given]) — the employee and the number of the document packet for which you need to find out information. The document packets are numbered started from 1 in the chronological order.
It is guaranteed that the input has at least one query of the third type.

Output
For each query of the third type print “YES” if the employee signed the document package and “NO” otherwise. Print all the words without the quotes.

Example
Input
4 9
1 4 3
2 4
3 3 1
1 2 3
2 2
3 1 2
1 3 1
2 2
3 1 3
Output
YES
NO
YES

/*``
题意:好复杂2333嗯。。自己看看吧大概也看得懂。。。2333(其实是我忘记了=-=)
思路:对同一个文件的查询,查询的关系树应该是2操作时的关系树,所以对每个2操作 我们可以把所有的对当时文件的查询一起处理。这里学到了个很厉害的东西。用dfs标记一个点入栈退栈的时间,用一个时间戳Index来计算,那么如果有一对点u,v,v在u到根节点的路径上,即r->v->u;那么必定存在 l[v]<l[u] r[v]>r[u]。由于1操作对关系图中的时间戳不会产生影响,并且需要将对同一文件的查询一起处理,所以我们先把所有操作读入,然后一起操作。2333
对每个1操作,我们用并查集合并x,y,然后先将所有3操作根据文件标号存好,对每个2操作去查询得到当前文件的人和被查询的人是不是同属一个集合,如果属于一个集合说明已经被合并如果同时满足lr的关系那么说明yes 不然no2333
*/

#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<vector>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<string>
#include<stack>
#include<map>
using namespace std;

//thanks to pyf ...

#define INF 0x3f3f3f3f
#define CLR(x,y) memset(x,y,sizeof(x))
#define mp(x,y) make_pair(x,y)
typedef pair<int,int> PII;
typedef long long ll;

const int N = 1e5+5;

int fa[N];
vector<int>G[N];//存图
vector<PII>Q[N];//用来存放查询 first是查询的人 second是查询的编号
int l[N],r[N];//入退栈的时间
int ok[N];//yes or no
int vis[N];//用来标记是否有入度,没入度的是根节点
int Index = 0;//时间戳
int doc_id = 0;//文件标号
int n,m;
struct Q
{
    int op;
    int x,y;
}query[N];
void init()//初始化函数
{
    CLR(ok,0);
    CLR(vis,0);
    CLR(l,0);
    CLR(r,0);
    Index = 0;
    doc_id = 0;
    for(int i=0;i<=N;i++)
    {
        fa[i] = i;
        G[i].clear();
        Q[i].clear();
    }

}
int find(int x)
{
    if(fa[x]!=x)
        fa[x] = find(fa[x]);
    return fa[x];
}
void merge(int a,int b)
{
    a = find(a);
    b = find(b);
    if(a!=b)
        fa[a] = fa[b];
}
void get_query()//预处理所有操作
{
    scanf("%d",&m);
    for(int i=0;i<m;i++)
    {
        int op;
        scanf("%d",&query[i].op);
        if(query[i].op==1)
        //反向建图,这样由最高领导指向所有下属,在dfs时对根节点不会重复的去覆盖lr值
        {
            scanf("%d%d",&query[i].x,&query[i].y);
            G[query[i].y].push_back(query[i].x);
            vis[query[i].x] = 1; // 有入度 不是根节点
        }
        else if(query[i].op == 2)
        {
            scanf("%d",&query[i].x);
            query[i].y = ++ doc_id;
        }
        else
        {
            scanf("%d%d",&query[i].x,&query[i].y);
            Q[query[i].y].push_back(mp(query[i].x,i));
            //对同一个文件的查询存放在一起
        }
    }
}
void dfs(int u)//处理 lr
{
    vis[u] = 1;
    l[u] = ++ Index;
    for(int i=0;i<G[u].size();i++)
    {
        int v = G[u][i];
        dfs(v);
    }
    r[u] = ++Index;
}
int main()
{
    while(scanf("%d",&n)==1)
    {
        init();
        get_query();
        for(int i=1;i<=n;i++)
            if(!vis[i])
                dfs(i);
        for(int i=0;i<m;i++)
        {
            if(query[i].op == 1)
            {
                merge(query[i].x,query[i].y);
            }
            else if(query[i].op==2)
            {
                int get_p = query[i].x;
                int doc = query[i].y;
                for(int j=0;j<Q[doc].size();j++)
                //判断是否可行
                {
                    int p = Q[doc][j].first;
                    int id = Q[doc][j].second;
//                  cout << get_p << " " << p << " ";
//                  cout << l[get_p] << " " << l[p] << " " << r[get_p] << " " << r[p] << " " << find(get_p) << " " << find(p) << endl;
                    if(l[get_p]>=l[p]&&r[get_p]<=r[p]&&(find(get_p)==find(p)))
                        ok[id] = 1;
                }
            }
        }
        for(int i=0;i<m;i++)
            if(query[i].op == 3)
                ok[i]==1 ? cout << "YES" << endl : cout << "NO" << endl;
    }
    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值