POJ 1988 【并查集 妙用deep数组】

Description

Farmer John and Betsy are playing a game with N (1 <= N <= 30,000)identical cubes labeled 1 through N. They start with N stacks, each containing a single cube. Farmer John asks Betsy to perform P (1<= P <= 100,000) operation. There are two types of operations:
moves and counts.
* In a move operation, Farmer John asks Bessie to move the stack containing cube X on top of the stack containing cube Y.
* In a count operation, Farmer John asks Bessie to count the number of cubes on the stack with cube X that are under the cube X and report that value.

Write a program that can verify the results of the game.

Input

* Line 1: A single integer, P

* Lines 2..P+1: Each of these lines describes a legal operation. Line 2 describes the first operation, etc. Each line begins with a ‘M‘ for a move operation or a ‘C‘ for a count operation. For move operations, the line also contains two integers: X and Y.For count operations, the line also contains a single integer: X.

Note that the value for N does not appear in the input file. No move operation will request a move a stack onto itself.

Output

Print the output from each of the count operations in the same order as the input file.

Sample Input

6
M 1 6
C 1
M 2 4
M 2 6
C 3
C 4

Sample Output

1
0
2

题意:搬箱子和架箱子,如果箱子上面有箱子,则再不打乱顺序的条件下把整体搬过去

先普及下基础:并查集就是在路径压缩的条件下找根,每次在函数返回的时候,顺带把路上遇到的人的BOSS改为最后找到的祖宗编号。这样可以提高今后找到最高领导人(也就是树的祖先)的速度。然后再merge中判断是不是在一个连通分量,若不是,则连通,把一个连通图的根赋给另一个连通图,靠这样的方式去合并一个连通分量。

看了网上大牛的思路做出来的,在并查集的基础上加一个deep数组,用来存旧根的深度,旧根的深度等于新根上一次的节点数,这里就是一个线树,最后输出的时候,查根,减去改节点的深度,再减去本身,就得到下面的箱子,很巧妙啊,整体分为了根的节点数,本身,本身的深度三部分,思路实在巧妙。

如 给出这个数据:

#include<cstdio>
#include<iostream>
#include<cstring>
using namespace std;
const int maxn=30010;
int fa[maxn],son[maxn],deep[maxn];

void init()
{
    for(int i=1;i<maxn;i++)
    {
        fa[i]=i;
        son[i]=1;
        deep[i]=0;
    }
}
int find(int x)
{
    if(x==fa[x]) return x;

    int tmp=fa[x];
    fa[x]=find(tmp);
    deep[x]+=deep[tmp];//fantasty

    return fa[x];
}

void join(int a,int b)//amazing
{
    int rt1=find(a);
    int rt2=find(b);
    if(rt1 != rt2)
    {
        fa[rt2]=rt1;
        deep[rt2]=son[rt1];
        son[rt1]+=son[rt2];
    }
}

int main()
{
    int p;
    char op[5];
    int a,b;
    int q;

    scanf("%d",&p);
    init();
    for(int i=0;i<p;i++)
    {
        scanf("%s",op);
        if(op[0]==‘M‘)
        {
            scanf("%d%d",&a,&b);
            join(a,b);
        }
        else
        {
            scanf("%d",&q);
            printf("%d\n",son[find(q)]-deep[q]-1);
        }
    }
}

/* 题意: 说是有n块砖,编号从1到n,有两种操作,第一是把含有x编号的那一堆砖放到含有编号y的那一堆砖的上面,
第二是查询编号为x的砖的下面有多少块砖。用count[x]表示下面有多少块砖。

  现在需要把两堆砖合并,显然要用上并查集,可是普通的合并之后如何知道x的下面有多少块砖呢,
思考合并的过程,对于一堆砖,移动到另一堆砖上时,上面那一堆上每块砖的count[i]应该加上下面一堆砖的数量,
这个操作对于上面一堆砖的根来说是简单的,我使用uset[i]表示连通分量,舒适化时所有的uset[i]为-1,负数代表这个节点为根,
1代表这个连通分量的节点总数为1,以样例为例,首先将1放到6上面,即将6合并到1所在的连通分量中,合并的过程中我们知道两个信息,
第一是当前连通分量6->1的节点数量为2,6距离1的距离为1,同理,将2放到4上面,这个连通分量节点个数为2,,4到2的距离为1
最后,我们将包含6的这个连通分量合并到包含2的这个连通分量中,
此时连通分量数为4,曾经的6->1连通分量的根距离合并后的连通分量的根的距离为2,就是4->2的连通分量的节点数

说了半天有什么用处呢,经过上面这个过程,
我们知道了每一个节点到它第一次被合并时的那个根节点的距离,6->1的距离为1,1到4的距离为2,2到4的距离为1,
这样我们在查询4的下面有多少块砖时,直接用4(连通分量节点数)-(1+2)(6到根节点的距离)-1=2

****************************************************

不用说肯定用并查集,貌似就是不进行路径压缩的无脑模拟?不对,500000个操作,30000艘船,不超时才怪!
那怎么办呢?一路径压缩战舰顺序就被改变,怎么才能在路径压缩的同时随时得知同一舰队中两艘战舰的位置?
输入是合并与询问两艘战舰之间的“距离”,遇到问题是路径压缩后“距离”(间隔战舰数量)变了,不压缩太慢了,那我们不就可以再开一个数组,存下需要的“距离”了吗?这个数组存的距离就是在路径压缩时变化了的那个:第i艘到第fa[i]艘之间的战舰数量,数组名就命名成front吧,因为路径压缩全部完成,即同一舰队中所有元素的fa[i]都等于这个舰队第一艘战舰时,front[i]=第i艘战舰前方有多少战舰(这么搞就像前缀和,路径压缩时可以一层一层地边压缩边修正下去)。

易知在还未进行路径压缩时对于同一舰队非第一艘战舰,front[i]=1,第一艘战舰front[i]=0(一个舰队的第一艘,不是编号为一的那艘),路径压缩首先不断向fa[i]走fa[i]=find(fa[i]),走到队首,fa[i]==i,front[i]=0不变,返回队首的编号,回溯至递归上一层,把队首的编号那么第二艘的front增加0就是1,为何是增加呢?因为前面说过完成路径压缩即find函数跑完后front[i]的值就是编号i的战舰前方有几艘战舰,路径压缩前则是到fa[i]的距离,一路径压缩,就相当于fa[i]直接越过front[fa[i]]艘战舰,从i的前一艘指向队首,fa[i]前进那么多,front[i]自然也要增加那么多,修改之后继续回溯,同理第三层front[i]+=front[fa[i]]……路径压缩完成。

合并时怎么办呢,定义合并函数uni(x,y)表示将x所在那列移到y所在那列后面(千万别搞反了),那么我们就要先找到两列的队首(依然用x、y存),像普通并查集那样fa[x]=y,然后维护front数组,这时遇到问题啦——front[y]要加多少呢?显然是x那列的战舰数,难道还要循环一遍统计一下吗?那太慢了,存下来吧,于是num[i]表示编号i这列的战舰总数(i是队首,不然每合并一次要修改的太多了,查询时num[i]时先find(i)找到队首吧),front[y]+=num[x],num[x]+=num[y],合并完成。

还有一个问题就是询问。对于一组询问ask(x,y),先找到他们的队首fx=find(x);fy=find(y);(顺便把路径压缩进行完全了,不用担心front[i]被重复增加了,路径压缩完全时front[fa[i]]==0,因为fa[i]就是队首呀),然后判断fx!=fy就输出-1,否则就输出abs(front[x]-front[y])-1(到队首的距离之差减一就是他们间隔距离)。

*/

路径压缩-节点到根节点的距离题目

时间: 2024-12-10 01:08:59

POJ 1988 【并查集 妙用deep数组】的相关文章

poj 1988 并查集(终于看懂一个了/(ㄒoㄒ)/~~)

题意:有几个stack,初始里面有一个cube.支持两种操作:1.move x y: 将x所在的stack移动到y所在stack的顶部.2.count x:数在x所在stack中,在x之下的cube的个数. 链接:点我 1 /* 2 POJ 1988 3 */ 4 #include <stdio.h> 5 #include <iostream> 6 #include <algorithm> 7 #include <string.h> 8 using name

poj 1988(并查集)

题意:有30000个木块,编号从1到30000,然后有两种操作M a b,把木块a所在的堆块放到木块b所在的堆块上,操作C a,询问木块a下面有多少个木块. 题解:用一个数组s[i]存木块i所在堆块一共有多少个木块,因为要求木块i下面有多少个木块,所以再添加一个数组dis[i]存木块i到根节点有多少个木块,这样res = s[i]-dis[i]-1.dis数组更新放在寻找根结点递归的后面,因为要先更新父亲的再更新自己的. #include <stdio.h> const int N = 300

POJ 2524 并查集

Ubiquitous Religions Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 23580 Accepted: 11609 Description There are so many different religions in the world today that it is difficult to keep track of them all. You are interested in finding o

poj 1703(并查集)

题意:有n个人和两个阵营,给出两种操作,A a b ,询问a和b的是否在同一个阵营,如果有人未加入任何阵营输入不确定.D a b,让a和b成为不同阵营的. 题解:两种做法. 一种是把并查集扩大一倍,先给每个人一个假定的敌人,然后有D操作时把敌人加入到假定敌人的集合中,A操作判断a和b是否在同一个集合,如果不在判断对方是否在自己假定敌人的集合中,在就是敌人,否则是不确定. #include <stdio.h> #include <string.h> const int N = 100

poj 2492(并查集)

题意:有一个科学家提出了一个假设,一种虫子只有异性恋,而不是同性恋,然后开始实验来验证猜想,给出了n个虫子,编号从1到n,给出了q组恋爱虫子的编号,问是否验证猜想. 题解:数组倍增,给每个虫子一个异性恋的对象集合,然后每次输入的两个虫子如果不在同一个集合内,就放到对方异性恋集合内,否则无法验证猜想. #include <stdio.h> const int N = 4005; int n, q, pa[N]; int get_parent(int x) { return x == pa[x]

poj 2513 并查集,Trie(字典树), 欧拉路径

- Colored Sticks POJ - 2513 You are given a bunch of wooden sticks. Each endpoint of each stick is colored with some color. Is it possible to align the sticks in a straight line such that the colors of the endpoints that touch are of the same color?

POJ 2492 并查集扩展(判断同性恋问题)

G - A Bug's Life Time Limit:10000MS     Memory Limit:65536KB     64bit IO Format:%I64d & %I64u Submit Status Practice POJ 2492 Appoint description: Description BackgroundProfessor Hopper is researching the sexual behavior of a rare species of bugs. H

poj 1417 并查集+dp

转自:点我 题目:给出p1+p2个人,其中p1个是好人,p2个是坏人.然后有一些关系 ,a说b是好人(坏人).其中没有矛盾的,判断是否有唯一解判断哪些人是好人,哪些人是坏人. 其中比较重要的是,好人总说真话,坏人总说假话.不需要判断矛盾.唯一解 http://poj.org/problem?id=1417 其中好人说真话,坏人说假话这点很重要. 那么如果一个人说另一个人是好人,那么如果这个人是好人,说明 对方确实是好人,如果这个是坏人,说明这句话是假的,对方也是坏人. 如果一个人说另一个人是坏人

POJ 2492 并查集应用的扩展

A Bug's Life Time Limit: 10000MS Memory Limit: 65536K Total Submissions: 28651 Accepted: 9331 Description Background Professor Hopper is researching the sexual behavior of a rare species of bugs. He assumes that they feature two different genders and