codeforces:855D Rowena Ravenclaw's Diadem分析和实现

  题目大意:

  提供n个对象,分别编号为1,...,n。每个对象都可能是某个编号小于自己的对象的特例或是成分。认为某个对象的特例的特例依旧是该对象的特例,即特例关系传递,同样一个对象的成分的成分依旧是该对象的成分。但是还需要注意一个对象的成分是该对象的所有特例的成分。每个对象都不可能是自己的特例或成分。要求解答之后的q个谜题,每个谜题提问两个对象是否是特例关系或成分关系。

  输入级别:n,q<1e5



  花了一个晚上才想到思路解了题目。。。

  首先我们要先解决一个简单的问题,我将这道题目这样描述,对于一株多叉树,如果进行预处理,保证能后续以O(1)的时间复杂度回答任意两个两个结点中是否某个结点存在于另外一个结点的子树中(我称这样的关系为衍生关系)。

  。。。

  好,思考时间结束!做法很简单,我们以先序对所有结点进行遍历,同时维护一个整数计数器,每次访问一个结点,就给这个结点分配当前计数器持有的值作为ID,同时令计数器自增。显然每一个结点都会分配到不同的ID值,且任意一个结点的子树中的编号都是连续的,即结点的子树的ID集合为一个正整数区间,而判断另外一个结点是否位于该结点的子树中,只需要判断其ID是否落于后者的子树ID区间中。这样这种衍生关系判断就能在O(1)时间复杂度内解决,而分配ID也只需要O(t)的时间复杂度,t为多叉树中的结点数。

  接下来我们将问题进行修改,假如认为对象的成分不会共享给其特例,即对象的成分不认为是成分的特例,那么如何在O(1)时间复杂度内解决一个谜题?

  很简单,这时候对象的成分关系和特例关系被分离了开来,可以单独计算。这时候可以分别按照成分关系和特例关系建立两株多叉树,并用上面所说的方式进行预处理。这样成分关系和特例关系都可以用衍生关系来表示,即一个对象是另外一个对象的特例,等价于在按特例关系建立的多叉树中前者衍生于后者(即前者落在后者的子树上)。

  到目前一切都很顺利,但是这只是因为没有将成分关系和特例关系混合在一起而已,如果真这么简单就好了。特例关系不受到成分关系的影响,可以直接套用前面的方法进行处理,但是成分关系变得相当负责。似乎在黑暗中找不到前进的方向一样,困惑了相当久,突然灵光一现:

  是的,这道题目的弱点就在于每个对象或者继承了另外一个对象,或者是另外一个对象的成分,当然也可能二者全无。可以利用这个特性说明下述命题。

  定义:若一个对象是另外一个对象的特例,我们称后者是前者的基。

  定义:若一个对象是另外一个对象的成分,我们称后者是前者的祖先。

  定义:若一个对象没有基,称该对象为基底。

  定义:若一个对象没有祖先,则称该对象为最祖先。

  命题1:对于任意对象A和B,若A是B的成分,则A或者直接是B的成分,或者是B的某个基的成分。

  证明:相当简单的命题,可以由题意直接得到,无需证明。

  命题2:对于任意对象A和B,若A是B的成分,则A或者直接是B的成分,或者B是A的唯一最祖先的特例,或者A是B的唯一基底的成分。

  证明:只需要证明若A是B的某个基的成分等价于“或者B是A的唯一最祖先的特例,或者A是B的唯一基底的成分。”。实际上A若是B的基的成分,只有三种可能,A是B的基底的成分,B是A的最祖先的特例,A是B的某个非基底的基C的成分且C有祖先。但是题目已经说明了一个非基底的对象(即是另外一个对象的特例)是不允许作为其它对象的成分的。因此第三种可能性被删除,只有前面两种可能性保存,命题得证。 

  利用命题2,我们只需要维护每个对象的基底和最祖先,就可以最多通过两次直接的成分关系判断(即在无视特例关系的情况下判断二者是否具有成分关系)和一次特例关系判断来判断对象之间的正确的成分关系。利用前面所述的分配ID的方式可以在O(1)时间复杂度内完成。而维护每个对象的基底和最祖先的工作非常容易,只需要令对象的特例继承该对象的基底,并设置自己的最祖先为某个空对象(或者自己,只需代码逻辑稍微变动),而对象成分关系,令成分继承祖先的最祖先,而自己的基底设置为空对象(或者自己),这些都可以在读取输入的时候附带完成,时间复杂度为O(1)。

  到此,我们预处理的时间复杂度为O(n),后面q次时间复杂度为O(1)的解谜,因此总的时间复杂度为O(n+q),空间复杂度由于维护了树形关系因此为O(n)。



  下面提供代码:

  

  1 package cn.dalt.codeforces;
  2
  3 import java.io.BufferedInputStream;
  4 import java.io.IOException;
  5 import java.io.InputStream;
  6 import java.io.PushbackInputStream;
  7 import java.math.BigDecimal;
  8 import java.util.ArrayList;
  9 import java.util.List;
 10
 11 /**
 12  * Created by Administrator on 2017/11/23.
 13  */
 14 public class RowenaRavenclawsDiadem {
 15     private static final String YES = "YES\n";
 16     private static final String NO = "NO\n";
 17     int n;
 18     Node[] nodes;
 19     AcmInputReader reader;
 20
 21     public static void main(String[] args) throws IOException {
 22         RowenaRavenclawsDiadem solution = new RowenaRavenclawsDiadem();
 23         solution.init();
 24         String result = solution.solve();
 25         System.out.println(result);
 26     }
 27
 28     public void init() throws IOException {
 29         reader = new AcmInputReader(System.in);
 30
 31         n = reader.nextInteger();
 32
 33         nodes = new Node[n];
 34
 35         for (int i = 0; i < n; i++) {
 36             nodes[i] = new Node();
 37
 38             int parent = reader.nextInteger() - 1;
 39             int relation = reader.nextInteger();
 40
 41             switch (relation) {
 42                 //No relation
 43                 case -1:
 44                     nodes[i].ancestor = nodes[i];
 45                     nodes[i].eldestBrother = nodes[i];
 46                     break;
 47                 //Brother
 48                 case 0:
 49                     nodes[i].eldestBrother = nodes[parent].eldestBrother;
 50                     nodes[i].ancestor = nodes[i];
 51                     nodes[parent].brothers.add(nodes[i]);
 52                     break;
 53                 //Parent
 54                 case 1:
 55                     nodes[i].eldestBrother = nodes[i];
 56                     nodes[i].ancestor = nodes[parent].ancestor;
 57                     nodes[parent].children.add(nodes[i]);
 58                     break;
 59             }
 60         }
 61
 62         //Allocate the num
 63         int[] horizontalNum = new int[]{0};
 64         int[] verticalNum = new int[]{0};
 65         for (Node node : nodes) {
 66             horizontalVisit(node, horizontalNum);
 67             verticalVisit(node, verticalNum);
 68         }
 69     }
 70
 71     public String solve() throws IOException {
 72         StringBuilder result = new StringBuilder();
 73
 74         int q = reader.nextInteger();
 75         for (int i = 0; i < q; i++) {
 76             int type = reader.nextInteger();
 77             int u = reader.nextInteger() - 1;
 78             int v = reader.nextInteger() - 1;
 79
 80
 81             switch (type) {
 82                 //Brother test
 83                 case 1:
 84                     result.append(u != v && nodes[u].isBrotherOf(nodes[v]) ? YES : NO);
 85                     break;
 86                 //Ancestor test
 87                 case 2:
 88                     result.append(
 89                             u != v && !nodes[v].isBrotherOf(nodes[u]) &&
 90                                     (nodes[v].ancestor == nodes[u]
 91                                             || nodes[v].ancestor.isBrotherOf(nodes[u])
 92                                             || nodes[u].eldestBrother.isAncestorOf(nodes[v]))
 93                                     ? YES : NO
 94                     );
 95                     break;
 96             }
 97         }
 98
 99         return result.toString();
100     }
101
102     public void horizontalVisit(Node node, int[] horizontalVal) {
103         if (node == null || node.horizontalRange.hasBeenInitialized()) {
104             return;
105         }
106
107         node.horizontalRange.begin = ++horizontalVal[0];
108         for (Node brother : node.brothers) {
109             horizontalVisit(brother, horizontalVal);
110         }
111         node.horizontalRange.end = horizontalVal[0];
112     }
113
114     public void verticalVisit(Node node, int[] verticalVal) {
115         if (node == null || node.verticalRange.hasBeenInitialized()) {
116             return;
117         }
118
119         node.verticalRange.begin = ++verticalVal[0];
120         for (Node child : node.children) {
121             verticalVisit(child, verticalVal);
122         }
123         node.verticalRange.end = verticalVal[0];
124     }
125
126     static class Node {
127         public Node eldestBrother;
128         public Node ancestor;
129         public List<Node> brothers = new ArrayList<>();
130         public List<Node> children = new ArrayList<>();
131
132         Range horizontalRange = new Range();
133         Range verticalRange = new Range();
134
135         public boolean isBrotherOf(Node other) {
136             return horizontalRange.contain(other.horizontalRange.begin - 1);
137         }
138
139         public boolean isAncestorOf(Node other) {
140             return verticalRange.contain(other.verticalRange.begin - 1);
141         }
142     }
143
144     static class Range {
145         int begin = -1;
146         int end = -1;
147
148         public boolean hasBeenInitialized() {
149             return begin != -1;
150         }
151
152         public boolean contain(int other) {
153             return other >= begin && other < end;
154         }
155     }
156
157
158     /**
159      * @author dalt
160      * @see java.lang.AutoCloseable
161      * @since java1.7
162      */
163     static class AcmInputReader implements AutoCloseable {
164         private PushbackInputStream in;
165
166         /**
167          * 创建读取器
168          *
169          * @param input 输入流
170          */
171         public AcmInputReader(InputStream input) {
172             in = new PushbackInputStream(new BufferedInputStream(input));
173         }
174
175         @Override
176         public void close() throws IOException {
177             in.close();
178         }
179
180         private int nextByte() throws IOException {
181             return in.read() & 0xff;
182         }
183
184         /**
185          * 如果下一个字节为b,则跳过该字节
186          *
187          * @param b 被跳过的字节值
188          * @throws IOException if 输入流读取错误
189          */
190         public void skipByte(int b) throws IOException {
191             int c;
192             if ((c = nextByte()) != b) {
193                 in.unread(c);
194             }
195         }
196
197         /**
198          * 如果后续k个字节均为b,则跳过k个字节。这里{@literal k<times}
199          *
200          * @param b     被跳过的字节值
201          * @param times 跳过次数,-1表示无穷
202          * @throws IOException if 输入流读取错误
203          */
204         public void skipByte(int b, int times) throws IOException {
205             int c;
206             while ((c = nextByte()) == b && times > 0) {
207                 times--;
208             }
209             if (c != b) {
210                 in.unread(c);
211             }
212         }
213
214         /**
215          * 类似于{@link #skipByte(int, int)}, 但是会跳过中间出现的空白字符。
216          *
217          * @param b     被跳过的字节值
218          * @param times 跳过次数,-1表示无穷
219          * @throws IOException if 输入流读取错误
220          */
221         public void skipBlankAndByte(int b, int times) throws IOException {
222             int c;
223             skipBlank();
224             while ((c = nextByte()) == b && times > 0) {
225                 times--;
226                 skipBlank();
227             }
228             if (c != b) {
229                 in.unread(c);
230             }
231         }
232
233         /**
234          * 读取下一块不含空白字符的字符块
235          *
236          * @return 下一块不含空白字符的字符块
237          * @throws IOException if 输入流读取错误
238          */
239         public String nextBlock() throws IOException {
240             skipBlank();
241             StringBuilder sb = new StringBuilder();
242             int c = nextByte();
243             while (AsciiMarksLazyHolder.asciiMarks[c = nextByte()] != AsciiMarksLazyHolder.BLANK_MARK) {
244                 sb.append((char) c);
245             }
246             in.unread(c);
247             return sb.toString();
248         }
249
250         /**
251          * 跳过输入流中后续空白字符
252          *
253          * @throws IOException if 输入流读取错误
254          */
255         private void skipBlank() throws IOException {
256             int c;
257             while ((c = nextByte()) <= 32) ;
258             in.unread(c);
259         }
260
261         /**
262          * 读取下一个整数(可正可负),这里没有对溢出做判断
263          *
264          * @return 下一个整数值
265          * @throws IOException if 输入流读取错误
266          */
267         public int nextInteger() throws IOException {
268             skipBlank();
269             int value = 0;
270             boolean positive = true;
271             int c = nextByte();
272             if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.SIGN_MARK) {
273                 positive = c == ‘+‘;
274             } else {
275                 value = ‘0‘ - c;
276             }
277             c = nextByte();
278             while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
279                 value = (value << 3) + (value << 1) + ‘0‘ - c;
280                 c = nextByte();
281             }
282
283             in.unread(c);
284             return positive ? -value : value;
285         }
286
287         /**
288          * 判断是否到了文件结尾
289          *
290          * @return true如果到了文件结尾,否则false
291          * @throws IOException if 输入流读取错误
292          */
293         public boolean isMeetEOF() throws IOException {
294             int c = nextByte();
295             if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.EOF) {
296                 return true;
297             }
298             in.unread(c);
299             return false;
300         }
301
302         /**
303          * 判断是否在跳过空白字符后抵达文件结尾
304          *
305          * @return true如果到了文件结尾,否则false
306          * @throws IOException if 输入流读取错误
307          */
308         public boolean isMeetBlankAndEOF() throws IOException {
309             skipBlank();
310             int c = nextByte();
311             if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.EOF) {
312                 return true;
313             }
314             in.unread(c);
315             return false;
316         }
317
318         /**
319          * 获取下一个用英文字母组成的单词
320          *
321          * @return 下一个用英文字母组成的单词
322          */
323         public String nextWord() throws IOException {
324             StringBuilder sb = new StringBuilder(16);
325             skipBlank();
326             int c;
327             while ((AsciiMarksLazyHolder.asciiMarks[(c = nextByte())] & AsciiMarksLazyHolder.LETTER_MARK) != 0) {
328                 sb.append((char) c);
329             }
330             in.unread(c);
331             return sb.toString();
332         }
333
334         /**
335          * 读取下一个长整数(可正可负),这里没有对溢出做判断
336          *
337          * @return 下一个长整数值
338          * @throws IOException if 输入流读取错误
339          */
340         public long nextLong() throws IOException {
341             skipBlank();
342             long value = 0;
343             boolean positive = true;
344             int c = nextByte();
345             if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.SIGN_MARK) {
346                 positive = c == ‘+‘;
347             } else {
348                 value = ‘0‘ - c;
349             }
350             c = nextByte();
351             while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
352                 value = (value << 3) + (value << 1) + ‘0‘ - c;
353                 c = nextByte();
354             }
355             in.unread(c);
356             return positive ? -value : value;
357         }
358
359         /**
360          * 读取下一个浮点数(可正可负),浮点数是近似值
361          *
362          * @return 下一个浮点数值
363          * @throws IOException if 输入流读取错误
364          */
365         public float nextFloat() throws IOException {
366             return (float) nextDouble();
367         }
368
369         /**
370          * 读取下一个浮点数(可正可负),浮点数是近似值
371          *
372          * @return 下一个浮点数值
373          * @throws IOException if 输入流读取错误
374          */
375         public double nextDouble() throws IOException {
376             skipBlank();
377             double value = 0;
378             boolean positive = true;
379             int c = nextByte();
380             if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.SIGN_MARK) {
381                 positive = c == ‘+‘;
382             } else {
383                 value = c - ‘0‘;
384             }
385             c = nextByte();
386             while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
387                 value = value * 10.0 + c - ‘0‘;
388                 c = nextByte();
389             }
390
391             if (c == ‘.‘) {
392                 double littlePart = 0;
393                 double base = 1;
394                 c = nextByte();
395                 while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
396                     littlePart = littlePart * 10.0 + c - ‘0‘;
397                     base *= 10.0;
398                     c = nextByte();
399                 }
400                 value += littlePart / base;
401             }
402             in.unread(c);
403             return positive ? value : -value;
404         }
405
406         /**
407          * 读取下一个高精度数值
408          *
409          * @return 下一个高精度数值
410          * @throws IOException if 输入流读取错误
411          */
412         public BigDecimal nextDecimal() throws IOException {
413             skipBlank();
414             StringBuilder sb = new StringBuilder();
415             sb.append((char) nextByte());
416             int c = nextByte();
417             while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
418                 sb.append((char) c);
419                 c = nextByte();
420             }
421             if (c == ‘.‘) {
422                 sb.append(‘.‘);
423                 c = nextByte();
424                 while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
425                     sb.append((char) c);
426                     c = nextByte();
427                 }
428             }
429             in.unread(c);
430             return new BigDecimal(sb.toString());
431         }
432
433         private static class AsciiMarksLazyHolder {
434             public static final byte BLANK_MARK = 1;
435             public static final byte SIGN_MARK = 1 << 1;
436             public static final byte NUMERAL_MARK = 1 << 2;
437             public static final byte UPPERCASE_LETTER_MARK = 1 << 3;
438             public static final byte LOWERCASE_LETTER_MARK = 1 << 4;
439             public static final byte LETTER_MARK = UPPERCASE_LETTER_MARK | LOWERCASE_LETTER_MARK;
440             public static final byte EOF = 1 << 5;
441             public static byte[] asciiMarks = new byte[256];
442
443             static {
444                 for (int i = 0; i <= 32; i++) {
445                     asciiMarks[i] = BLANK_MARK;
446                 }
447                 asciiMarks[‘+‘] = SIGN_MARK;
448                 asciiMarks[‘-‘] = SIGN_MARK;
449                 for (int i = ‘0‘; i <= ‘9‘; i++) {
450                     asciiMarks[i] = NUMERAL_MARK;
451                 }
452                 for (int i = ‘a‘; i <= ‘z‘; i++) {
453                     asciiMarks[i] = LOWERCASE_LETTER_MARK;
454                 }
455                 for (int i = ‘A‘; i <= ‘Z‘; i++) {
456                     asciiMarks[i] = UPPERCASE_LETTER_MARK;
457                 }
458                 asciiMarks[0xff] = EOF;
459             }
460         }
461     }
462 }

codeforces:855D Rowena Ravenclaw's Diadem分析和实现

时间: 2024-10-30 14:01:38

codeforces:855D Rowena Ravenclaw's Diadem分析和实现的相关文章

Codeforces 777A Shell Game

A. Shell Game time limit per test:0.5 seconds memory limit per test:256 megabytes input:standard input output:standard output Bomboslav likes to look out of the window in his room and watch lads outside playing famous shell game. The game is played b

codeforces 710D Two Arithmetic Progressions(线性同余方程)

题目链接: http://codeforces.com/problemset/problem/710/D 分析:给你两个方程 a1k + b1 and a2l + b2,求在一个闭区间[L,R]中有多少个X,X满足 x = a1k' + b1 = a2l' + b2. 由此可以发现这两个方程满足线性同余,即 x ≡b1mod(a1) 且 x≡b2mod(a2); 也就是 a1k' + b1 = a2l' + b2a. 所以 a1k1 + (-a2k2) = (b2 - b1),由同余方程得 :

Codeforces Round #415 (Div. 2)(A,暴力,B,贪心,排序)

A. Straight «A» time limit per test:1 second memory limit per test:256 megabytes input:standard input output:standard output Noora is a student of one famous high school. It's her final year in school — she is going to study in university next year.

Codeforces Round #365 (Div. 2) C

链接:http://codeforces.com/problemset/problem/703/C 分析:  这题其实是个大水题,只要短短的几行就可以搞定的, 首先需要直觉判断一点,就是只要判断最左边的点和最下面的点 以及和他们相邻边的右边的点 第二部我们就需要判断人与这两个边有没有交点,如果人与上面的有交点,那么人肯定就需要在下面的那条边等待  等待的时间是 max(x - y*1.0*v/u) 那么我们所需要做的还有一步就是判断就是需不需要等待 ,当min(x - y*1.0*v/u) >=

codeforces 710E Generate a String(简单dp)

传送门:http://codeforces.com/problemset/problem/710/E 分析: 让你写一个全由"a"组成的长为n的串,告诉你两种操作,第一种:插入一个字母或者删除一个字母需要花费x秒, 第二种:复制现有的串,并加入到原来的穿上,花费y秒,问你最少花费多少时间? 用dp[i]表示需要花费的最少时间,‘ 然后对i为偶数的情况取min(dp[i-1] +x,dp[i/2]+y),当i为奇数时min(dp[i-1] + x, min(dp[i/2+1] + y +

Educational Codeforces Round 73

1334 (-2), pupilRank: 2668 (虽然比上次好,但是摆脱不了掉分的命运...) D. Make The Fence Great Again http://codeforces.com/contest/1221/problem/D 分析:对每个高度来说,最多升高两次,因此可以对这个进行dp(一开始读错题了,没有看到是相邻不能同高,一开始以为所有都不能同高) 代码: 1 #include <bits/stdc++.h> 2 using namespace std; 3 con

Codeforces Round #599 (Div. 2)D 边很多的只有0和1的MST

题:https://codeforces.com/contest/1243/problem/D 分析:找全部可以用边权为0的点连起来的全部块 然后这些块之间相连肯定得通过边权为1的边进行连接 所以答案就是这些块的总数-1: #include<bits/stdc++.h> using namespace std; typedef long long ll; #define pb push_back const int M=1e5+5; set<int>s,g[M]; int vis[

April Fools Contest 2017 题解&amp;源码(A,数学 B,数学 C,数学 D,字符串 E,数字逻辑 F,排序,卡时间)

A. Numbers Joke time limit per test:2 seconds memory limit per test:64 megabytes input:standard input output:standard output Input The input contains a single integer a (1 ≤ a ≤ 30). Output Output a single integer. Example Input 3 Output 27 题目链接:http

AIM Tech Round 4 (Div. 2)(A,暴力,B,组合数,C,STL+排序)

A. Diversity time limit per test:1 second memory limit per test:256 megabytes input:standard input output:standard output Calculate the minimum number of characters you need to change in the string s, so that it contains at least k different letters,