codeforces:Helga Hufflepuff's Cup
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了codeforces:Helga Hufflepuff's Cup相关的知识,希望对你有一定的参考价值。
题目大意:有一个包含n个顶点的无向无环连通图G,图中每个顶点都允许有一个值type,type的范围是1~m。有一个特殊值k,若一个顶点被赋值为k,则所有与之相邻的顶点只能被赋小于k的值。最多有x个顶点被赋值为k。求问有多少种不同的赋值方案。
这是一道树形DP的题目。由于是无环无向连通图,因此可以任选一个顶点R作为根结点,从而构造一颗树TREE。为每个顶点N维护一个属性maybe[3][x+1]。其中maybe[0][i]表示当N被赋小于k的值时,N及其所有后代结点总共出现i个被赋值为k的结点的总共组合数。而maybe[1][i]表示当N被赋值为k时,N及其所有后代结点总共出现i个被赋值为k的结点的总共组合数。maybe[2][i]表示当N被赋值大于k时,N及其所有后代结点总共出现i个被赋值为k的结点的总共组合数。
若A、B、C三个结点是独立的,且A有a种状态,B有b种状态,C有c种状态,那么仅考虑A、B、C的情况下最多有abc种状态。因此可以看出状态数的计算应该是结合的。同样若A有a1种状态1和a2种状态2,且B有b1种状态3和b2种状态4,且当A为状态1与B为状态3是互斥的,而A为状态2与B为状态4是互斥的,那么仅考虑A与B最多有a1b2+a2b1种状态。
计算某个结点N的maybe数组的流程可以总结如下:先创建一个与maybe等大的数组statePre和statePost。statePre用于记录前置状态,而statePost用于记录后置状态。一开始先将statePre初始化为仅一个N结点时的可能状态数目。之后遍历所有子结点,对于每一个子结点S进行下述操作:
statePost[0][i] = statePre[0][0] * (S.maybe[0][i] + S.maybe[1][i] + S.maybe[2][i]) + ... + statePre[0][i] * (S.maybe[0][0] + S.maybe[1][0] + S.maybe[2][0])
statePost[1][i] = statePre[1][0] * S.maybe[0][i] + ... statePre[1][i] * S.maybe[0][0]
statePost[2][i] = statePre[2][0] * (S.maybe[0][i] + S.maybe[2][i]) + ... + statePre[2][i] * (S.maybe[0][0] + S.maybe[2][0])
之后用用statePost的值覆盖statePre,并清空statePost。直到所有子结点全部遍历完毕,maybe即为statePre。
而最终结果是累加R[0][0], ... , R[0][x], R[1][0], ... , R[1][x], R[2][0], ... ,R[2][x]得到的加总,即各种情况下组合数的总和。
每个顶点有且仅有一个父顶点,而顶点的maybe数组在参与到父亲的maybe数组的计算过程总共时间费用为O(3*x^2)。而顶点总数是n,因此总的时间复杂度为O(3*n*x^2)。
下面给出JAVA代码:
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/9/24. 13 */ 14 public class HelgaHufflepuffsCup { 15 16 final int LESS_THAN_K = 0; 17 final int EQUAL_TO_K = 1; 18 final int GREATER_THAN_K = 2; 19 int n; 20 int m; 21 int k; 22 int x; 23 Node[] allNodes; 24 long[][] backup; 25 long modulo = 1000000000 + 7; 26 int lessInitVal; 27 int equalInitVal; 28 int greaterInitVal; 29 30 public static void main(String[] args) throws Exception { 31 HelgaHufflepuffsCup solution = new HelgaHufflepuffsCup(); 32 solution.init(); 33 long result = solution.solve(); 34 System.out.println(result); 35 } 36 37 public void init() throws Exception { 38 AcmInputReader input = new AcmInputReader(System.in); 39 n = input.nextInteger(); 40 m = input.nextInteger(); 41 42 allNodes = new Node[n]; 43 for (int i = 0; i < n; i++) { 44 allNodes[i] = new Node(); 45 allNodes[i].id = i + 1; 46 } 47 for (int i = 1; i < n; i++) { 48 int n1 = input.nextInteger() - 1; 49 int n2 = input.nextInteger() - 1; 50 allNodes[n1].nearBy.add(allNodes[n2]); 51 allNodes[n2].nearBy.add(allNodes[n1]); 52 } 53 54 k = input.nextInteger(); 55 x = input.nextInteger(); 56 } 57 58 public long solve() { 59 //Mark allNodes[0] as the root of tree 60 backup = new long[3][x + 1]; 61 62 lessInitVal = k - 1; 63 equalInitVal = 1; 64 greaterInitVal = m - k; 65 66 detect(allNodes[0], null); 67 68 long sum = 0; 69 for (int i = 0; i < 3; i++) { 70 for (int j = 0; j <= x; j++) { 71 sum += allNodes[0].maybe[i][j]; 72 } 73 } 74 return sum % modulo; 75 } 76 77 public void detect(Node node, Node parent) { 78 long[][] maybe = new long[3][x + 1]; 79 80 maybe[0][0] = lessInitVal; 81 maybe[1][1] = equalInitVal; 82 maybe[2][0] = greaterInitVal; 83 84 for (Node nearby : node.nearBy) { 85 if (nearby == parent) { 86 continue; 87 } 88 89 detect(nearby, node); 90 combineInto(backup, maybe, nearby.maybe); 91 { 92 long[][] tmp = backup; 93 backup = maybe; 94 maybe = tmp; 95 } 96 } 97 98 node.maybe = maybe; 99 } 100 101 public void combineInto(long[][] result, long[][] father, long[][] kid) { 102 for (int i = 0; i <= x; i++) { 103 long result0i = 0; 104 long result1i = 0; 105 long result2i = 0; 106 for (int j = 0; j <= i; j++) { 107 result0i += (father[0][i - j] * (kid[0][j] + kid[1][j] + kid[2][j])) % modulo; 108 result1i += (father[1][i - j] * kid[0][j]) % modulo; 109 result2i += (father[2][i - j] * (kid[0][j] + kid[2][j])) % modulo; 110 } 111 result[0][i] = result0i % modulo; 112 result[1][i] = result1i % modulo; 113 result[2][i] = result2i % modulo; 114 } 115 } 116 117 118 static class Node { 119 List<Node> nearBy = new ArrayList<>(); 120 long[][] maybe; 121 int id; 122 123 @Override 124 public String toString() { 125 StringBuilder sb = new StringBuilder(); 126 sb.append(id); 127 sb.append("\n"); 128 if (maybe != null) { 129 for (int i = 0; i < 3; i++) { 130 for (int j = 0; j < maybe[i].length; j++) { 131 sb.append(maybe[i][j]); 132 sb.append(", "); 133 } 134 sb.setLength(sb.length() - 2); 135 sb.append("\n"); 136 } 137 } 138 return sb.toString(); 139 } 140 } 141 142 /** 143 * @author dalt 144 * @see java.lang.AutoCloseable 145 * @since java1.7 146 */ 147 static class AcmInputReader implements AutoCloseable { 148 private PushbackInputStream in; 149 150 /** 151 * 创建读取器 152 * 153 * @param input 输入流 154 */ 155 public AcmInputReader(InputStream input) { 156 in = new PushbackInputStream(new BufferedInputStream(input)); 157 } 158 159 @Override 160 public void close() throws IOException { 161 in.close(); 162 } 163 164 private int nextByte() throws IOException { 165 return in.read() & 0xff; 166 } 167 168 /** 169 * 如果下一个字节为b,则跳过该字节 170 * 171 * @param b 被跳过的字节值 172 * @throws IOException if 输入流读取错误 173 */ 174 public void skipByte(int b) throws IOException { 175 int c; 176 if ((c = nextByte()) != b) { 177 in.unread(c); 178 } 179 } 180 181 /** 182 * 如果后续k个字节均为b,则跳过k个字节。这里{@literal k<times} 183 * 184 * @param b 被跳过的字节值 185 * @param times 跳过次数,-1表示无穷 186 * @throws IOException if 输入流读取错误 187 */ 188 public void skipByte(int b, int times) throws IOException { 189 int c; 190 while ((c = nextByte()) == b && times > 0) { 191 times--; 192 } 193 if (c != b) { 194 in.unread(c); 195 } 196 } 197 198 /** 199 * 类似于{@link #skipByte(int, int)}, 但是会跳过中间出现的空白字符。 200 * 201 * @param b 被跳过的字节值 202 * @param times 跳过次数,-1表示无穷 203 * @throws IOException if 输入流读取错误 204 */ 205 public void skipBlankAndByte(int b, int times) throws IOException { 206 int c; 207 skipBlank(); 208 while ((c = nextByte()) == b && times > 0) { 209 times--; 210 skipBlank(); 211 } 212 if (c != b) { 213 in.unread(c); 214 } 215 } 216 217 /** 218 * 读取下一块不含空白字符的字符块 219 * 220 * @return 下一块不含空白字符的字符块 221 * @throws IOException if 输入流读取错误 222 */ 223 public String nextBlock() throws IOException { 224 skipBlank(); 225 StringBuilder sb = new StringBuilder(); 226 int c = nextByte(); 227 while (AsciiMarksLazyHolder.asciiMarks[c = nextByte()] != AsciiMarksLazyHolder.BLANK_MARK) { 228 sb.append((char) c); 229 } 230 in.unread(c); 231 return sb.toString(); 232 } 233 234 /** 235 * 跳过输入流中后续空白字符 236 * 237 * @throws IOException if 输入流读取错误 238 */ 239 private void skipBlank() throws IOException { 240 int c; 241 while ((c = nextByte()) <= 32) ; 242 in.unread(c); 243 } 244 245 /** 246 * 读取下一个整数(可正可负),这里没有对溢出做判断 247 * 248 * @return 下一个整数值 249 * @throws IOException if 输入流读取错误 250 */ 251 public int nextInteger() throws IOException { 252 skipBlank(); 253 int value = 0; 254 boolean positive = true; 255 int c = nextByte(); 256 if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.SIGN_MARK) { 257 positive = c == ‘+‘; 258 } else { 259 value = ‘0‘ - c; 260 } 261 c = nextByte(); 262 while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) { 263 value = (value << 3) + (value << 1) + ‘0‘ - c; 264 c = nextByte(); 265 } 266 267 in.unread(c); 268 return positive ? -value : value; 269 } 270 271 /** 272 * 判断是否到了文件结尾 273 * 274 * @return true如果到了文件结尾,否则false 275 * @throws IOException if 输入流读取错误 276 */ 277 public boolean isMeetEOF() throws IOException { 278 int c = nextByte(); 279 if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.EOF) { 280 return true; 281 } 282 in.unread(c); 283 return false; 284 } 285 286 /** 287 * 判断是否在跳过空白字符后抵达文件结尾 288 * 289 * @return true如果到了文件结尾,否则false 290 * @throws IOException if 输入流读取错误 291 */ 292 public boolean isMeetBlankAndEOF() throws IOException { 293 skipBlank(); 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 下一个用英文字母组成的单词 306 */ 307 public String nextWord() throws IOException { 308 StringBuilder sb = new StringBuilder(16); 309 skipBlank(); 310 int c; 311 while ((AsciiMarksLazyHolder.asciiMarks[(c = nextByte())] & AsciiMarksLazyHolder.LETTER_MARK) != 0) { 312 sb.append((char) c); 313 } 314 in.unread(c); 315 return sb.toString(); 316 } 317 318 /** 319 * 读取下一个长整数(可正可负),这里没有对溢出做判断 320 * 321 * @return 下一个长整数值 322 * @throws IOException if 输入流读取错误 323 */ 324 public long nextLong() throws IOException { 325 skipBlank(); 326 long value = 0; 327 boolean positive = true; 328 int c = nextByte(); 329 if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.SIGN_MARK) { 330 positive = c == ‘+‘; 331 } else { 332 value = ‘0‘ - c; 333 } 334 c = nextByte(); 335 while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) { 336 value = (value << 3) + (value << 1) + ‘0‘ - c; 337 c = nextByte(); 338 } 339 in.unread(c); 340 return positive ? -value : value; 341 } 342 343 /** 344 * 读取下一个浮点数(可正可负),浮点数是近似值 345 * 346 * @return 下一个浮点数值 347 * @throws IOException if 输入流读取错误 348 */ 349 public float nextFloat() throws IOException { 350 return (float) nextDouble(); 351 } 352 353 /** 354 * 读取下一个浮点数(可正可负),浮点数是近似值 355 * 356 * @return 下一个浮点数值 357 * @throws IOException if 输入流读取错误 358 */ 359 public double nextDouble() throws IOException { 360 skipBlank(); 361 double value = 0; 362 boolean positive = true; 363 int c = nextByte(); 364 if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.SIGN_MARK) { 365 positive = c == ‘+‘; 366 } else { 367 value = c - ‘0‘; 368 } 369 c = nextByte(); 370 while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) { 371 value = value * 10.0 + c - ‘0‘; 372 c = nextByte(); 373 } 374 375 if (c == ‘.‘) { 376 double littlePart = 0; 377 double base = 1; 378 c = nextByte(); 379 while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) { 380 littlePart = littlePart * 10.0 + c - ‘0‘; 381 base *= 10.0; 382 c = nextByte(); 383 } 384 value += littlePart / base; 385 } 386 in.unread(c); 387 return positive ? value : -value; 388 } 389 390 /** 391 * 读取下一个高精度数值 392 * 393 * @return 下一个高精度数值 394 * @throws IOException if 输入流读取错误 395 */ 396 public BigDecimal nextDecimal() throws IOException { 397 skipBlank(); 398 StringBuilder sb = new StringBuilder(); 399 sb.append((char) nextByte()); 400 int c = nextByte(); 401 while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) { 402 sb.append((char) c); 403 c = nextByte(); 404 } 405 if (c == ‘.‘) { 406 sb.append(‘.‘); 407 c = nextByte(); 408 while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) { 409 sb.append((char) c); 410 c = nextByte(); 411 } 412 } 413 in.unread(c); 414 return new BigDecimal(sb.toString()); 415 } 416 417 private static class AsciiMarksLazyHolder { 418 public static final byte BLANK_MARK = 1; 419 public static final byte SIGN_MARK = 1 << 1; 420 public static final byte NUMERAL_MARK = 1 << 2; 421 public static final byte UPPERCASE_LETTER_MARK = 1 << 3; 422 public static final byte LOWERCASE_LETTER_MARK = 1 << 4; 423 public static final byte LETTER_MARK = UPPERCASE_LETTER_MARK | LOWERCASE_LETTER_MARK; 424 public static final byte EOF = 1 << 5; 425 public static byte[] asciiMarks = new byte[256]; 426 427 static { 428 for (int i = 0; i <= 32; i++) { 429 asciiMarks[i] = BLANK_MARK; 430 } 431 asciiMarks[‘+‘] = SIGN_MARK; 432 asciiMarks[‘-‘] = SIGN_MARK; 433 for (int i = ‘0‘; i <= ‘9‘; i++) { 434 asciiMarks[i] = NUMERAL_MARK; 435 } 436 for (int i = ‘a‘; i <= ‘z‘; i++) { 437 asciiMarks[i] = LOWERCASE_LETTER_MARK; 438 } 439 for (int i = ‘A‘; i <= ‘Z‘; i++) { 440 asciiMarks[i] = UPPERCASE_LETTER_MARK; 441 } 442 asciiMarks[0xff] = EOF; 443 } 444 } 445 } 446 }
以上是关于codeforces:Helga Hufflepuff's Cup的主要内容,如果未能解决你的问题,请参考以下文章