codeforces:219D. Choosing Capital for Treeland

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了codeforces:219D. Choosing Capital for Treeland相关的知识,希望对你有一定的参考价值。

  题目大意:国家由n个城市以及n-1条连接不同城市的道路组成(每条道路都有正向和逆向之分),并且每个城市到另外一个城市都至少存在一条路径。现在议会要决定选一个城市作为首都。当一个城市选为首都时,需要将所有从首都到其它城市的路径上的所有边都是正向的(如果不是正向的则需要颠转道路)。求这样的首都,使得需要颠转的道路数目最小。

  其中2<=n<=2e5。

 


  首先这显然是一副无向无环连通图(参考我的博客连通图的一些性质)。因此从任意一个城市出发到另外一个城市都有唯一一条路径。

  为了后面分析的简便,这里记选取城市x为首都需要颠转的道路数目为x的费用,记作x.c。需要考虑这样一个思路,假设A和B是两个之间存在道路的城市,且道路由A指向B(即从A到B是正向的),那么城市A和B必定有这样的关系:A.c + 1= B.c。说明一下为什么,当选取A作为首都时,尝试将所有需要颠转的边进行颠转,共颠转了A.c条边,此时可以认为所有从A出发到其它城市的路径中只包含正向边。而当选取B作为首都时,那么我们只需要在上面的基础上再颠转A与B之间的边。对于原本所有从A出发到达的城市的路径中包含B的路径终点C,由于路径上只有正向边,因此从B到C也可以只沿着正向边到达;而对于原本所有从A出发到达的城市的路径中不包含B的路径终点D,由于此时B,A,...,D这些城市之间均由正向边相连,故B到D的路径只有正向边;对于城市A,由于B,A是正向(颠转的结果),故B到A的路径只有正向边。故我们得出最多颠转A.c+1条边就可以保证B作为首都,即B.c <= A.c + 1。而对于B作为首都,颠转了共B.c条边,那么当选取A作为首都时,只需要恢复A与B之间的边即可使得A到达其它城市的路径只有正向边,而这里的证明与前面基本一致,得出的结论是A.c <= B.c - 1,而合并的结论是A.c + 1= B.c。

  利用前面的分析的结果,利用n-1条边的方向,我们可以得出n个城市的关系。选取任意一个城市X做为参照,利用广度优先搜索算法在O(n)的时间复杂度内可以计算剩余n-1个城市相对于城市X的费用,而同样利用广度优先搜索算法可以在O(n)的时间复杂度内计算出城市X的费用。最后的输出只需要组装相对费用和X的费用即可得出。总的时间复杂度为O(n),而空间复杂度由于使用了广度优先搜索和保存边的信息,因此为O(n)。

  


下面给出代码:

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

 

以上是关于codeforces:219D. Choosing Capital for Treeland的主要内容,如果未能解决你的问题,请参考以下文章

codeforces:219D. Choosing Capital for Treeland

Codeforces 219D Choosing Capital for Treeland:Tree dp

Codeforces 219D Choosing Capital for Treeland?????????DP???

Codeforces 219D Choosing Capital for Treeland 2次DP

Choosing Capital for Treeland CodeForces - 219D (树形DP)

CodeForces 219D Choosing Capital for Treeland (树形DP)