HDU 5923 并查集

版权声明:欢迎转载,不要求署名~~~ https://blog.csdn.net/shadandeajian/article/details/81950579

传送门:题目

题意:

题目比较难理解,读懂之后就是个并查集,别的博客用的可持久化并查集,本人就用了个普通并查集,也能AC,因为不会可持久化并查集啊。
有一个图,一棵树,然后树中的每个结点存的都是图中的边的信息,所以图有m条边,则树就有m个顶点。然后有q次问询,每次问询都给几个点,从树中找到这些点,并且他们的所有祖先也要加入这个集合,然后把这个集合映射到图中,求图的连通分量。答案就是连通分量的个数

题解:

  • 不难发现,根结点一定是所有祖先的一员,我们只要DFS一遍,然后遍历路径就是问询的集合,然后我们考虑怎么求连通分量。
  • 我们可以用并查集考虑连通分量的个数,因为根节点相同的,肯定处在同一个连通分量。
  • 我们可以在DFS让子节点继承父节点的并查集,这样Query的时候,我们只需要考虑所有节点的并查集就好了,然后两个for循环,第一个for扫描每一个query,第二个for扫描图中每一个结点,然后再一次并查集,最后再用一个for扫一遍就行了,有几个根节点就有几个连通分量。
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
#define debug(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f
using namespace std;

const int maxv = 510;//图的顶点数
const int maxe = 10010;//图的边数

int n, m, t1, t2;
int head[maxe], cnt; //建树相关变量,这里的head上限是边的个数,因为树的点代表边的个数,一开始这里开小了,疯狂MLE
int root[maxe][maxv];//并查集相关变量
bool vis[maxv]; int Query, query[maxe], qcnt, ans; //查询相关变量
struct Edge {
    int to, next;
} edge[maxe];
pair<int, int> pii;
vector<pair<int, int> > vpii;

void init() {
    memset(head, -1, sizeof head);
    vpii.clear();
    cnt = 0;
    for (int i = 0; i <= m; i++)
        for (int j = 0; j <= n; j++)
            root[i][j] = j;//图上每个点的根节点都初始化为自己,并查集
}
void init_Query() {
    memset(vis, false, sizeof vis);
    ans = 0;
}
void addedge(int from, int to) {
    edge[cnt].to = to;
    edge[cnt].next = head[from];
    head[from] = cnt++;
}
int Find(int tree_point, int graph_point) {//并查集
    if (root[tree_point][graph_point] == graph_point)
        return graph_point;
    return root[tree_point][graph_point] = Find(tree_point, root[tree_point][graph_point]);
}
void mix(int tree_point, int graph_point1, int graph_point2) {//并查集
    graph_point1 = Find(tree_point, graph_point1);
    graph_point2 = Find(tree_point, graph_point2);
    if (graph_point1 != graph_point2)
        root[tree_point][graph_point2] = graph_point1;
}
void dfs(int u) {
    for (int i = head[u]; i != -1; i = edge[i].next) {
        int v = edge[i].to;
        memcpy(root[v], root[u], sizeof root[v]);
        mix(v, vpii[v - 1].first, vpii[v - 1].second);
        dfs(v);
    }
}
int main(void) {
    int T, kase = 1;
    scanf("%d", &T);
    while (T--) {
        printf("Case #%d:\n", kase++);
        scanf("%d%d", &n, &m);
        init();
        addedge(0, 1);
        for (int i = 2; i <= m; i++)
            scanf("%d", &t1), addedge(t1, i); //建树
        for (int i = 0; i < m; i++) //构图
            scanf("%d%d", &t1, &t2), pii = make_pair(t1, t2), vpii.push_back(pii);
        dfs(0);
        scanf("%d", &Query);
        while (Query--) {
            init_Query();
            scanf("%d", &qcnt);
            for (int i = 0; i < qcnt; i++)
                scanf("%d", &query[i]);

            memcpy(root[m + 1], root[query[0]], sizeof root[m + 1]);//因为有Query次查询,所有不能破环已经建好的并查集,所有要新开辟一行进行并查集
            for (int i = 1; i < qcnt; i++)//扫描每一个query
                for (int j = 1; j <= n; j++)//扫描图上每一个点
                    mix(m + 1, Find(query[i], j), j);//可以合并就合并
            for (int i = 1; i <= n; i++)
                if (!vis[Find(m + 1, i)])
                    vis[Find(m + 1, i)] = true, ans++;

            cout << ans << endl;
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/shadandeajian/article/details/81950579