Fork me on GitHub

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

题意:有三种操作:

1.新增一条边从y连向x,此前x没有父节点

2.x接到一份文件,(文件标号逐次递增),然后将这份文件一路上溯,让所有上溯的节点都接到这份文件

3.查询某个节点x是否接到过文件F

解法:

首先要知道一个性质,节点u在v的上溯路径上的话要满足: L[u]<=L[v] && R[u] >= R[v] (先进后出)

先将所有的边都读入,dfs得出L[u],R[u],然后将查询分为tot类(tot=总文件种数),记录每一类有那些地方查询了,然后如果type=2,那么记录这个type=2现在是第几类文件,都存下来以后备用。

再从1枚举到m,枚举每个输入,

如果type=1,那么将x,y集合合并,就算是建立了父子关系。

如果type=2,那么先得出现在得到的文件种类,再得到这个种类的所有查询,处理所有查询,得到u,v,先判断是否在一个集合中,然后再进行L[u]<=L[v] && R[u] >= R[v] 的判断,如果成立,说明u接到了v传来的该种类的文件。

如果type=3,先不处理,但是按照题意,也可以直接输出了。

最后输出。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
#define N 100107

vector<int> G[N],query[N];
int fa[N],L[N],R[N],vis[N],Time,doc[N],f[N];
struct node{
    int op,x,y;
}Q[N];

int findset(int x) {
    if(x != fa[x]) fa[x] = findset(fa[x]);
    return fa[x];
}

void dfs(int u) {
    vis[u] = 1;
    L[u] = ++Time;
    for(int i=0;i<G[u].size();i++) {
        int v = G[u][i];
        dfs(v);
    }
    R[u] = ++Time;
}

int main()
{
    int n,m,i,op,DOC = 0,docnum;
    memset(vis,0,sizeof(vis));
    scanf("%d%d",&n,&m);
    for(i=0;i<=n;i++) fa[i] = i;
    for(i=1;i<=m;i++) {
        scanf("%d",&Q[i].op);
        if(Q[i].op == 1) {
            scanf("%d%d",&Q[i].x,&Q[i].y);
            G[Q[i].y].push_back(Q[i].x);
            vis[Q[i].x] = 1;
        }
        else if(Q[i].op == 2)  {
            scanf("%d",&Q[i].x);
            doc[i] = ++DOC;     //第i个查询是查询第DOC种文档
        }
        else {
            scanf("%d%d",&Q[i].x,&docnum);
            query[docnum].push_back(i);  //查询第docnum种文档的查询标号
        }
    }
    Time = 0;
    memset(f,0,sizeof(f));
    for(i=1;i<=n;i++)
        if(!vis[i]) dfs(i);  //处理出L[u],R[u]
    for(i=1;i<=m;i++) {
        if(Q[i].op == 1) {
            int fx = findset(Q[i].x);
            int fy = findset(Q[i].y);
            fa[fx] = fy;
        }
        else if(Q[i].op == 2) {
            int now = doc[i];     //是第几种文档
            int v = Q[i].x;       //最底层签发者
            for(int j=0;j<query[now].size();j++) {
                int ind = query[now][j];
                int u = Q[ind].x;    //查询目标
                if(findset(u) == findset(v) && L[u] <= L[v] && R[u] >= R[v])
                    f[ind] = 1;
            }
        }
    }
    for(i=1;i<=m;i++) if(Q[i].op == 3) {
        if(f[i]) puts("YES");
        else     puts("NO");
    }
    return 0;
}
View Code

 

posted @ 2015-01-17 21:15  whatbeg  阅读(305)  评论(0编辑  收藏  举报