UVALive 6910 Cutting Tree(并查集应用)

  总体来说,这个题给的时间比较长,样例也是比较弱的,别的方法也能做出来。

  我第一次使用的是不合并路径的并查集,几乎是一种暴力,花了600多MS,感觉还是不太好的,发现AC的人很多都在300MS之内的过得。

  看到他们的做法后,我知道了这个题比较好的做法。

  逆向思维的并查集,因为这里只有去边操作,完全可以离线计算,把删边当成加边,然后逆序输出答案即可。

  但是,这个却有一个坑,很坑,只有第一次删除的时候,我们才对他进行操作,加边的时候也只能在第一次删除的时候加。当时因为这里,十分困惑……

  这是我无路径压缩的并查集的做法:

  

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
using namespace std;
#define N 20005
int n,fa[N];
int Find(int x){
    while(x != fa[x]){
        x = fa[x];
    }
    return x;
}
int main() {
//    freopen("E.in.cpp","r",stdin);
    int T,k,ca=0,p,a,b;
    scanf("%d",&T);
    char op[2];
    while(T--) {
        scanf("%d%d",&n,&k);
        for(int i = 1; i <= n; i++) {
            fa[i] = i;
        }
        for(int i = 1; i <= n; i++) {
            scanf("%d",&p);
            if(p == 0) fa[i] = i;
            else {
                fa[i] = p;
            }
        }
        printf("Case #%d:\n",++ca);
        while(k--) {
            scanf("%s",op);
            if(op[0] == ‘C‘) {
                scanf("%d",&b);
                if(fa[b] == b) continue;
                fa[b] = b;
            } else {
                scanf("%d%d",&a,&b);
                if(Find(a) != Find(b)) {
                    printf("NO\n");
                } else printf("YES\n");
            }
        }
    }
    return 0;
}

  这是逆序离线并查集的做法:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
using namespace std;
const int N = 2e4+5;
int n,fa[N],pa[N],vis[N];
string ans[5005];
struct Query {
    int a,b,kind;
    void Set(int A,int B,int k) {
        a = A;
        b = B;
        kind = k;
    }
} q[5005];
int Find(int x) {
    return fa[x]==x? x :fa[x]=Find(fa[x]);
}
void Union(int a,int b) {
    fa[Find(b)] = Find(a);
}
int main() {
//    freopen("E.in.cpp","r",stdin);
    int T,k,ca=0;
    char op[3];
    scanf("%d",&T);
    while(T--) {
        scanf("%d%d",&n,&k);
        for(int i = 1; i <= n; i++) {
            scanf("%d",&pa[i]);
            fa[i] = i;
            vis[i] = 0;
        }
        printf("Case #%d:\n",++ca);
        int a,b;
        for(int i = 0; i < k; i++) {
            scanf("%s",op);
            if(op[0] == ‘C‘) {
                scanf("%d",&a);
                if(pa[a]!=0 && vis[a] != 1) q[i].Set(a,0,1);
                else q[i].Set(a,0,-1);
                vis[a] = 1;
            } else {
                scanf("%d%d",&a,&b);
                q[i].Set(a,b,0);
            }
        }
        for(int i = 1; i <= n; i++) {
            if(vis[i]==1 || pa[i]==0) continue;
            Union(pa[i],i);
        }
        int cnt = 0;
        for(int i = k-1; i >= 0; i--) {
            if(q[i].kind == -1) continue;
            else if(q[i].kind == 0) {
                if(Find(q[i].a) == Find(q[i].b)) ans[cnt++] = "YES";
                else ans[cnt++] = "NO";
            } else  Union(pa[q[i].a],q[i].a);
        }
        for(int i = cnt-1; i >= 0; i--) {
            cout<<ans[i]<<endl;
        }
    }
    return 0;
}
时间: 2024-10-12 20:10:49

UVALive 6910 Cutting Tree(并查集应用)的相关文章

UVALive 4487 Exclusive-OR 加权并查集神题

已知有 x[0-(n-1)],但是不知道具体的值,题目给定的信息 只有 I P V,说明 Xp=V,或者 I P Q V,说明 Xp ^ Xq=v,然后要求回答每个询问,询问的是 某任意的序列值 Xp1^Xp2,,,,X^pk 这个题目用加权并查集是这么处理的: 1. f[]照样是代表父节点,照样进行路径压缩,把每个 V[i]=V[i]^V[f[i]],即节点存储的值实际是它与它父亲的异或的值.为什么要这样呢,因为异或首先满足交换律,而且异或同一个数偶数次,即相当于本身,那么这个题目的其中一个要

HDU 1325 Is It A Tree? 并查集

判断是否为树 森林不是树 空树也是树 成环不是树 数据: 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9 1 0 0 1 2 2 3 4 5 0 0 2 5 0 0 ans: no no yes #include <stdio.h> #include <string.h> #include <stdlib.h> #include <limits.h> #include <malloc.h> #include <ctype

HDU1325 Is It A Tree? 并查集

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=1325 这题与HDU1272 小希的迷宫 (并查集) 非常像,不过细细看,还是有一点区别的.就是这题的路径是单向的,每次只能由起点指向终点,在连接之前终点必须是根节点. 注意的问题: 1.不能成环,即每次输入的两个数的根节点不能相同: 2.最终根节点数目为一 3.注意当只输入"0 0" 时要输出"Case %d is a tree." 4.路径是单向的,即每次只能由起点指

HDU 5606 tree 并查集

tree 把每条边权是1的边断开,发现每个点离他最近的点个数就是他所在的连通块大小. 开一个并查集,每次读到边权是0的边就合并.最后Ans?i??=size[findset(i)],size表示每个并查集根的size Ans_i=size[findset(i)],sizeAns?i??=size[findset(i)],size表示每个并查集根的sizesize. #include<cstdio> #include<cstring> #include<algorithm>

HDU 1325 POJ 1308 Is It A Tree? (并查集)

这道题就是裸并查集,关键在于对不是树几种的判断 1. 空树是树 2. 森林不是树 3. 无环 或者从入度来看:1,无环:2,除了根,所有的入度为1,根入度为0:3,这个结构只有一个根,不然是森林了. 这道题本来暑假做的POJ 1308 但是HDU没有过.在于空树没有考虑. 用并查集判断有多少个森林注意编号是随机的,不是次序.... /* input: 0 0 1 1 0 0 1 2 1 2 0 0 1 2 2 3 4 5 0 0 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9

hdu5606 tree (并查集)

tree Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 823    Accepted Submission(s): 394 Problem Description There is a tree(the tree is a connected graph which contains n points and n−1 edges),t

swust oj 856--Huge Tree(并查集)

题目链接:http://acm.swust.edu.cn/problem/856/ Time limit(ms): 1000 Memory limit(kb): 10000 There are N trees in a forest. At first, each tree contains only one node as its root. And each node is marked with a number. You're asked to do the following two

[POJ 1308]Is It A Tree?(并查集判断图是否为一棵有根树)

Description A tree is a well-known data structure that is either empty (null, void, nothing) or is a set of one or more nodes connected by directed edges between nodes satisfying the following properties. There is exactly one node, called the root, t

CF109 C. Lucky Tree 并查集

Petya loves lucky numbers. We all know that lucky numbers are the positive integers whose decimal representations contain only the lucky digits 4 and 7. For example, numbers 47, 744, 4 are lucky and 5, 17, 467 are not. One day Petya encountered a tre