第一天:树与并查集

一、树

  1、概念

    根和深度

    儿子父亲子树森林

    叶子:没有儿子的节点

    爸爸:父亲的父亲
   2、 两点距离最短时过其公共祖先
   3、任意两点之间加一条线变成一个环

  4、直径:最远两点的路径

       任取点P,搜索到P距离最远的Q,再搜索离Q最远的W,直径为QW(证明)

二、二叉树

    1、左右儿子,左右子树,
      2、前序遍历:根,左子树,右子树
         中序遍历:左,根,右
              后序遍历:左,右,根
        给两种求第三种
      3、满二叉树 2^n-1个节点,最大深度为n
       完全二叉树:若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点到最大个数,第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。

三、并查集

  路径压缩

第一天:树与并查集_第1张图片

 

 

  •  测验
  • 题目:

    2003年4月16日,世界卫生组织根据包括中国内地和香港地区,加拿大、美国在内的11个国家和地区的13个实验室通力合作研究的结果,宣布重症急性呼吸综合征的病因是一种新型的冠状病毒,称为SARS冠状病毒。
    很不幸,小明同学昨天晚上被确诊以经感染非典病毒,为此,校医院需要对他隔离治疗并隔离观察与他直接或间接接触者。
    可以认为,同一个社团内如果有人感染病毒或可能感染病毒,那么这个社团内所有人都被认为是可能已经感染了病毒,由于时间紧迫,需要尽快找到所有可能携带病毒的同学并隔离,以防止更大范围的病毒扩散,院长请你帮忙编写程序计算需要隔离多少人

    Input

    输入文件包含多组数据。
    对于每组测试数据:
    第一行为两个整数n和m
    其中n是学生的数量,m是学生社团的数量。0 < n <= 30000 , 0 <= m <= 500
    每个学生编号是一个0到n-1之间的整数,已知小明同学编号是0
    紧随其后的是团体的成员列表,每组一行。
    每一行有一个整数k,代表成员数量。之后有k个整数代表这个群体的学生。一行中的所有整数由至少一个空格隔开。
    n = m = 0表示输入结束,不需要处理。

    Output

    对于每组测试数据
    输出需要隔离的人数,每组输出占一行

    Sample Input

    100 4
    2 1 2
    5 10 13 11 12 14
    2 0 1
    2 99 2
    200 2
    1 5
    5 1 2 3 4 5
    1 0
    0 0

   Sample Output

   4

   1

   1

  题解:典型并查集

  注意:fa【x】要初始化,n,m不要搞混

  ac代码:

 
#define M 30010
using namespace std;
int m,n,d,a[M],ans,fa[M],x,y;

int finda(int x)
{
    if(x==fa[x])
        return x;
    return fa[x]=finda(fa[x]);
}
int main()
{
    while(~scanf("%d%d",&m,&n))
    {
        if(m==0&&n==0)
            break;
        for(int i=0;i<=m;i++)
        {
            fa[i]=i;
        }
        ans=0;
        for(int i=0;i)
        {
            scanf("%d",&d);
            scanf("%d",&a[1]);
            for(int j=2;j<=d;j++)
            {
                scanf("%d",&a[j]);
                if(finda(a[j])!=finda(a[j-1]))
                    fa[finda(a[j])]=finda(a[j-1]);
            }
        }
        x=finda(0);
        for(int i=0;i)
        {
            y=finda(i);
            if(y==x)
            {
                ans++;
            }
        }
        printf("%d\n",ans);
    }

    return 0;
}

2.

动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形。A吃B, B吃C,C吃A。
现有N个动物,以1-N编号。每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种。
有人用两种说法对这N个动物所构成的食物链关系进行描述:
第一种说法是"1 X Y",表示X和Y是同类。
第二种说法是"2 X Y",表示X吃Y。
此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
1) 当前的话与前面的某些真的话冲突,就是假话;
2) 当前的话中X或Y比N大,就是假话;
3) 当前的话表示X吃X,就是假话。
你的任务是根据给定的N(1 <= N <= 50,000)和K句话(0 <= K <= 100,000),输出假话的总数。

Input

第一行是两个整数N和K,以一个空格分隔。
以下K行每行是三个正整数 D,X,Y,两数之间用一个空格隔开,其中D表示说法的种类。
若D=1,则表示X和Y是同类。
若D=2,则表示X吃Y。

Output

只有一个整数,表示假话的数目。

Sample Input

100 7
1 101 1 
2 1 2
2 2 3 
2 3 3 
1 1 3 
2 3 1 
1 5 5

Sample Output

3
题解:采用3*n来做,x为一种动物,x+n为x的食物,x+2*n为x的敌人,判断是否成立,不成立,continue,成立,link
注意:数组大小要为3倍,fa【x】初始化要到3*n
ac代码:
 1 #include
 2 #define M 200010
 3 using namespace std;
 4 int n,m,a,x,y,xx,yy,ans,fa[M];
 5 int finda(int x)
 6 {
 7     if(x==fa[x])
 8         return x;
 9     return fa[x]=finda(fa[x]);
10 }
11 void link(int x,int y)
12 {
13     int xx=finda(x),yy=finda(y);
14     if(xx!=yy)
15         fa[xx]=yy;
16 }
17 int main()
18 {
19     scanf("%d%d",&n,&m);
20     for(int i=0;i<=3*n;i++)
21     {
22         fa[i]=i;
23     }
24     for(int i=1;i<=m;i++)
25     {
26         scanf("%d%d%d",&a,&x,&y);
27         if(x>n || y>n || (a==2 &&x==y))
28         {
29             ans++;
30              //printf(" %d ",i);
31             continue;
32         }
33         if(a==1)
34         {
35             xx=finda(x);
36             yy=finda(y);
37             if(xx==finda(y+2*n) || yy==finda(x+2*n))
38             {
39                 ans++;
40                  //printf(" %d ",i);
41                 continue;
42             }
43             fa[xx]=yy;
44             link(x,y);
45             link(x+n,y+n);
46             link(x+2*n,y+2*n);
47         }
48         else if(a==2)
49         {
50             xx=finda(x);
51             yy=finda(y);
52             if(xx==yy ||finda(x+2*n)==yy)
53             {
54                 ans++;
55                  //printf(" %d ",i);
56                 continue;
57             }
58             link(x,y+2*n);
59             link(x+n,y);
60             link(x+2*n,y+n);
61         }
62     }
63     printf("%d\n",ans);
64     return 0;
65 }
View Code

3.

Little Valentine liked playing with binary trees very much. Her favorite game was constructing randomly looking binary trees with capital letters in the nodes.
This is an example of one of her creations:

D
/ \
/ \
B E
/ \ \
/ \ \
A C G
/
/
F

To record her trees for future generations, she wrote down two strings for each tree: a preorder traversal (root, left subtree, right subtree) and an inorder traversal (left subtree, root, right subtree). For the tree drawn above the preorder traversal is DBACEGF and the inorder traversal is ABCDEFG.
She thought that such a pair of strings would give enough information to reconstruct the tree later (but she never tried it).

Now, years later, looking again at the strings, she realized that reconstructing the trees was indeed possible, but only because she never had used the same letter twice in the same tree.
However, doing the reconstruction by hand, soon turned out to be tedious.
So now she asks you to write a program that does the job for her!

Input

The input will contain one or more test cases.
Each test case consists of one line containing two strings preord and inord, representing the preorder traversal and inorder traversal of a binary tree. Both strings consist of unique capital letters. (Thus they are not longer than 26 characters.)
Input is terminated by end of file.

Output

For each test case, recover Valentine's binary tree and print one line containing the tree's postorder traversal (left subtree, right subtree, root).

Sample Input

DBACEGF ABCDEFG
BCAD CBAD

Sample Output

ACBFGED
CDAB
题解:给出前序遍历,中序遍历求后序遍历
注意:字符串的输入不加&,判断数组边界
ac代码:
 1 #include
 2 #include
 3 #define M 50
 4 using namespace std;
 5 char a[M],b[M];
 6 int la,lb;
 7 void build(int l1,int r1,int l2,int r2)
 8 {
 9     if(l1>r1) return ;
10     char root=a[l1];
11     int q=l2,cnt=0;
12     if(l2<r2)
13     {
14         while(b[q++]!=root && q<=r2) cnt++;
15         build(l1+1,l1+cnt,l2,l2+cnt-1);
16         build(l1+cnt+1,r1,l2+1+cnt,r2);
17     }
18     printf("%c",root);
19 }
20 int main()
21 {
22     while(~scanf("%s%s",a+1,b+1))
23     {
24         la=strlen(a+1);
25         lb=strlen(b+1);
26         build(1,la,1,lb);
27         printf("\n");
28     }
29     return 0;
30 }
View Code

4.

Today is Ignatius' birthday. He invites a lot of friends. Now it's dinner time. Ignatius wants to know how many tables he needs at least. You have to notice that not all the friends know each other, and all the friends do not want to stay with strangers.

One important rule for this problem is that if I tell you A knows B, and B knows C, that means A, B, C know each other, so they can stay in one table.

For example: If I tell you A knows B, B knows C, and D knows E, so A, B, C can stay in one table, and D, E have to stay in the other one. So Ignatius needs 2 tables at least.

InputThe input starts with an integer T(1<=T<=25) which indicate the number of test cases. Then T test cases follow. Each test case starts with two integers N and M(1<=N,M<=1000). N indicates the number of friends, the friends are marked from 1 to N. Then M lines follow. Each line consists of two integers A and B(A!=B), that means friend A and friend B know each other. There will be a blank line between two cases.
OutputFor each test case, just output how many tables Ignatius needs at least. Do NOT print any blanks.
Sample Input

2
5 3
1 2
2 3
4 5

5 1
2 5

Sample Output

2
4
题解:并查集
ac代码
 1 #include
 2 #define M 1010
 3 using namespace std;
 4 int t,n,fa[M],vis[M],ans,x,y,m;
 5 
 6 int finda(int x)
 7 {
 8     if(x==fa[x])
 9     {
10         //printf("fa%d\n",fa[x]);
11         return x;
12     }
13     return fa[x]=finda(fa[x]);
14 }
15 int main()
16 {
17     scanf("%d",&t);
18     while(t--)
19     {
20         scanf("%d%d",&n,&m);
21         for(int i=1;i<=n;i++)
22         {
23             fa[i]=i;
24             vis[i]=0;
25         }
26         for(int i=1;i<=m;i++)
27         {
28             scanf("%d%d",&x,&y);
29             if(finda(x)!=finda(y))
30                 fa[finda(x)]=finda(y);
31         }
32         ans=0;
33         for(int i=1;i<=n;i++)
34         {
35             if(vis[finda(i)]==0)
36             {
37                 ans++;
38                // printf("find%d ",finda(i));
39                 vis[finda(i)]++;
40             }
41         }
42         printf("%d\n",ans);
43     }
44     return 0;
45 }
View Code

5.

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, to which no directed edges point.
Every node except the root has exactly one edge pointing to it.
There is a unique sequence of directed edges from the root to each node.
For example, consider the illustrations below, in which nodes are represented by circles and edges are represented by lines with arrowheads. The first two of these are trees, but the last is not.
第一天:树与并查集_第2张图片

In this problem you will be given several descriptions of collections of nodes connected by directed edges. For each of these you are to determine if the collection satisfies the definition of a tree or not.
Input
The input will consist of a sequence of descriptions (test cases) followed by a pair of negative integers. Each test case will consist of a sequence of edge descriptions followed by a pair of zeroes Each edge description will consist of a pair of integers; the first integer identifies the node from which the edge begins, and the second integer identifies the node to which the edge is directed. Node numbers will always be greater than zero.
Output
For each test case display the line "Case k is a tree." or the line "Case k is not a tree.", where k corresponds to the test case number (they are sequentially numbered starting with 1).
Sample Input
6 8  5 3  5 2  6 4
5 6  0 0

8 1  7 3  6 2  8 9  7 5
7 4  7 8  7 6  0 0

3 8  6 8  6 4
5 3  5 6  5 2  0 0
-1 -1
Sample Output
Case 1 is a tree.
Case 2 is a tree.
Case 3 is not a tree.
题解:看出度和入度,(考试的时候没写出来,考完也没改出来),看环,重复,0 0
ac代码

6.
After hearing about the epidemic of obesity in the USA, Farmer John wants his cows to get more exercise, so he has committed to create a bovine marathon for his cows to run. The marathon route will include a pair of farms and a path comprised of a sequence of roads between them. Since FJ wants the cows to get as much exercise as possible he wants to find the two farms on his map that are the farthest apart from each other (distance being measured in terms of total length of road on the path between the two farms). Help him determine the distances between this farthest pair of farms.
Input
* Lines 1.....: Same input format as "Navigation Nightmare".
Output
* Line 1: An integer giving the distance between the farthest pair of farms.
Sample Input
7 6
1 6 13 E
6 3 9 E
3 5 7 S
4 1 3 N
2 4 20 W
4 7 2 S
题解:可dfs,bfs,dp(dp还不会),建立图,add,edge
ac代码
 1 #include
 2 #include
 3 #include
 4 #define M 1000000
 5 using namespace std;
 6 int n,m,u,v,w,ans,p,q[M],cnt=1,head[M],vis[M],dis[M],fa[M],s;
 7 struct hhh
 8 {
 9     int to,nxt,w;
10 }edge[M];
11 void add(int u,int v,int w)
12 {
13     edge[cnt].to=v;
14     edge[cnt].w=w;
15     edge[cnt].nxt=head[u];
16     head[u]=cnt++;
17 }
18 void dfs(int x)
19 {
20     for(int i=head[x];i;i=edge[i].nxt)
21     {
22         int r=edge[i].to;
23         if(fa[x]==r)
24             continue;
25         fa[r]=x;
26         dis[r]=dis[x]+edge[i].w;
27         dfs(r);
28     }
29 }
30 int main()
31 {
32     scanf("%d%d",&n,&m);
33     memset(head,0,sizeof(head));
34     for(int i=0;i<=n;i++) fa[i]=i;
35     for(int i=1;i<=m;i++)
36     {
37         scanf("%d%d%d",&u,&v,&w);
38         add(u,v,w);
39         add(v,u,w);
40         getchar();
41         getchar();
42     }
43     dfs(1);
44     for(int i=0;i<=n;i++)
45     {
46         if(dis[i]>ans)
47         {
48             ans=dis[i];
49             p=i;
50         }
51         dis[i]=0;
52         fa[i]=i;
53     }
54 
55     printf("\n%d\n%d",p,ans);
56     dfs(p);
57     ans=0;
58     for(int i=0;i<=n;i++)
59     {
60         if(dis[i]>ans)
61             ans=dis[i];
62     }
63     printf("%d\n",ans);
64     return 0;
65 }
View Code

7.

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
题解:用祖先的子序列大小-所求值的深度,深度在finda()中更新
ac代码:
 1 #include
 2 #include
 3 #define N 31000
 4 using namespace std;
 5 int n,s[N],d[N],x,y,fa[N],xx,yy;
 6 char a;
 7 int finda(int x)
 8 {
 9     if(x==fa[x])
10         return x;
11     int tmp=fa[x];
12     fa[x]=finda(fa[x]);
13     d[x]+=d[tmp];
14     return fa[x];
15 }
16 int main()
17 {
18     scanf("%d",&n);
19    // memset(s,1,sizeof(s));
20     memset(d,0,sizeof(d));
21     for(int i=1;i)
22     {
23         fa[i]=i;
24         s[i]=1;
25         d[i]=0;
26     }
27     for(int i=1;i<=n;i++)
28     {
29         scanf(" %c",&a);
30         if(a=='M')
31         {
32             scanf("%d%d",&x,&y);
33             xx=finda(x);
34             yy=finda(y);
35             if(xx!=yy)
36             {
37                 fa[yy]=xx;
38                 d[yy]=s[xx];
39                 s[xx]+=s[yy];
40             }
41 
42         }
43         if(a=='C')
44         {
45             scanf("%d",&x);
46             printf("%d\n",s[finda(x)]-d[x]-1);
47         }
48         //getchar();
49     }
50     return 0;
51 }
View Code

 

 

你可能感兴趣的:(第一天:树与并查集)