[并查集两题]银河英雄传说,破译密文

1、银河英雄传说

题目描述

       公元五八○一年,地球居民迁移至金牛座α第二行星,在那里发表银河联邦创立宣言,同年改元为宇宙历元年,并开始向银河系深处拓展。 
      宇宙历七九九年,银河系的两大军事集团在巴米利恩星域爆发战争。泰山压顶集团派宇宙舰队司令莱因哈特率领十万余艘战舰出征,气吞山河集团点名将杨威利组织麾下三万艘战舰迎敌。 
杨威利擅长排兵布阵,巧妙运用各种战术屡次以少胜多,难免恣生骄气。在这次决战中,他将巴米利恩星域战场划分成30000列,每列依次编号为1, 2, …, 30000。之后,他把自己的战舰也依次编号为1, 2, …, 30000,让第i号战舰处于第i列(i = 1, 2, …, 30000),形成“一字长蛇阵”,诱敌深入。这是初始阵形。当进犯之敌到达时,杨威利会多次发布合并指令,将大部分战舰集中在某几列上,实施密集攻击。合并指令为M i j,含义为让第i号战舰所在的整个战舰队列,作为一个整体(头在前尾在后)接至第j号战舰所在的战舰队列的尾部。显然战舰队列是由处于同一列的一个或多个战舰组成的。合并指令的执行结果会使队列增大。 
然而,老谋深算的莱因哈特早已在战略上取得了主动。在交战中,他可以通过庞大的情报网络随时监听杨威利的舰队调动指令。 
      在杨威利发布指令调动舰队的同时,莱因哈特为了及时了解当前杨威利的战舰分布情况,也会发出一些询问指令:C i j。该指令意思是,询问电脑,杨威利的第i号战舰与第j号战舰当前是否在同一列中,如果在同一列中,那么它们之间布置有多少战舰。 
     作为一个资深的高级程序设计员,你被要求编写程序分析杨威利的指令,以及回答莱因哈特的询问。 
最终的决战已经展开,银河的历史又翻过了一页……

 

输入

输入的第一行有一个整数T(1<=T<=500,000),表示总共有T条指令。 
以下有T行,每行有一条指令。指令有两种格式: 
1.M i j :i和j是两个整数(1<=i , j<=30000),表示指令涉及的战舰编号。该指令是莱因哈特窃听到的杨威利发布的舰队调动指令,并且保证第i号战舰与第j号战舰不在同一列。 
2.C i j :i和j是两个整数(1<=i , j<=30000),表示指令涉及的战舰编号。该指令是莱因哈特发布的询问指令。

 

输出

       你的程序应当依次对输入的每一条指令进行分析和处理: 
       如果是杨威利发布的舰队调动指令,则表示舰队排列发生了变化,你的程序要注意到这一点,但是不要输出任何信息; 
      如果是莱因哈特发布的询问指令,你的程序要输出一行,仅包含一个整数,表示在同一列上,第i号战舰与第j号战舰之间布置的战舰数目。如果第i号战舰与第j号战舰当前不在同一列上,则输出-1。

 

样例输入

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

 

样例输出

   -1

 1

 

思路:题目的两个指令摆明了是并查集。和普通的并查集区别的是查询时需要输出查询两舰之间的舰数。这就要求了合并时要有序,但线性不优化的并查集肯定会超时,于是可以用一个数组s来储存当前战舰的权值,在每次合并时更新i列舰队的头的权值和父结点。在每次执行search操作时更新其他节点的权值和父结点。代码中b数组为当前列的集合大小,方便更新权值。

 

代码:

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int maxn = 3e4;
 4 int f[maxn+5], s[maxn+5], b[maxn+5];
 5 
 6 void init();
 7 int search(int);
 8 void merge(int, int);
 9 
10 int main()
11 {
12     int T;
13     scanf("%d\n",&T);
14     char op;
15     int x, y;
16     init();
17     while (T--)
18     {
19         scanf("%c %d %d\n",&op, &x, &y);
20         if (op == 'M')
21         {
22             merge(x, y);
23         }
24         else if (op == 'C')
25         {
26             if (search(x) == search(y))
27             {
28                 printf("%d\n",abs(s[x] - s[y]) - 1);
29             }
30             else
31             {
32                 printf("-1\n");
33             }
34         }
35     }
36     return 0;
37 }
38 void init()
39 {
40     for (int i=1; i<=maxn; i++)
41         f[i] = i, b[i] = 1, s[i] = 0;
42 }
43 int search(int x)
44 {
45     if (f[x] == x)
46         return x;
47     int k = f[x];
48     f[x] = search(f[x]);
49     s[x] += s[k];
50     b[x] = b[f[x]];
51     return f[x];
52 }
53 void merge(int x, int y)
54 {
55     int fx = search(x);
56     int fy = search(y);
57     f[fx] = fy;
58     s[fx] += b[fy];
59     b[fx] += b[fy];
60     b[fy] = b[fx];
61 }
View Code

 

 

2、破译密文

题目描述

信息的明文是由0和1组成的非空序列。但在网络通信中,为了信息的安全性,常对明文进行加密,用密文进行传输。密文是由0、1和若干个密码字母组成,每个密码字母代表不超过100位不同的01串,例如,密文=011a0bf00a01。密码破译的关键是确定每个密码的含义。

          经过长期统计分析,现在知道了每个密码的固定长度,如今,我方又截获了敌方的两段密文S1和S2,并且知道S1=S2,即两段密文代表相同的明文。你的任务是帮助情报人员对给定的两段密文进行分析,看一看有多少种可能的明文,保证密文是合法的,不用判错。

 

输入

第一行为一个字符串s1

第二行为字符串s2

第三行为整数n,表示有n个小写字母(0<n<=26)

接着n行,每行为一个字符和一个整数,字符的长度


输出

为一行,为共有多少中明文的可能


样例输入

100ad1
cc1
4
a 2
d 3
c 4
b 50

 

样例输出

2

 思路:把每个字母拆分成与其等长的字串,例如c1,c2,c3,c4,再一一对应将每一对对应的字符并入同一个集合,最后和0同集合的就是0,和1同集合的就是1,其他集合就是未知的,可以是0或1,答案就是2的未知集合数次方。因为我做的题目明确表示保证密文是合法的,但我在别的地方看到的题目并没有保证密文合法,所以补充判断密文不合法的要点:1、字符串不等长2、某两个有明确指定的对应字符不相同(即一个是1一个是0)。

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 const int maxn = 10010;
 4 char c, s1[maxn], s2[maxn];
 5 int n, l[27], cnt, zj[27][110], f[maxn];
 6 bool vis[maxn];
 7 vector <int> t1, t2;
 8 
 9 int find(int x);
10 void deal(char *s, int &len, vector <int> &a);
11 void init(int n);
12 
13 int main()
14 {
15     ios::sync_with_stdio(false);
16     cnt = 1;
17     cin >> s1 >> s2 >> n;
18     int l1 = strlen(s1), l2 = strlen(s2);
19     for (int i=1; i<=n; i++)
20     {
21         cin >> c;
22         cin >> l[c - 'a'];
23         for (int j=1; j<=l[c - 'a']; j++)
24         {
25             zj[c - 'a'][j] = ++cnt;
26         }
27     }
28     deal(s1, l1, t1);
29     deal(s2, l2, t2);
30     init(cnt);
31     for (int i=0;i<l1;i++) {
32         int x = t1[i], y = t2[i], fx = find(x), fy = find(y);
33         if (fx != fy)
34         {
35             if (fx == 1 || fx == 0)
36                 f[fy] = fx;
37             else
38                 f[fx] = fy;
39         }
40     }
41 
42     int ans = 0;
43     for (int i=2;i<=cnt;i++)
44         if (f[i] == i && vis[i]) ans++;
45     cout << (1 << ans) << endl;
46     return 0;
47 }
48 
49 int find(int x)
50 {
51     if (f[x] == x)
52         return x;
53     f[x] = find(f[x]);
54     return f[x];
55 }
56 void deal(char *s, int &len, vector <int> &a)
57 {
58     for (int i=0; i<=len; i++)
59     {
60         if (s[i] == '0' || s[i] == '1')
61             a.push_back(s[i]-'0');
62         else
63             for (int j=1; j<=l[s[i]-'a']; j++)
64             {
65                 vis[zj[s[i]-'a'][j]] = true;
66                 a.push_back(zj[s[i]-'a'][j]);
67             }
68     }
69     len = a.size();
70 }
71 void init(int n)
72 {
73     for (int i=0;i<=n;i++) {
74         f[i] = i;
75     }
76 }
View Code

 

posted @ 2020-04-10 21:45  FantaDevourer  阅读(228)  评论(0编辑  收藏  举报