题目大意:有一个包含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代码:

 package cn.dalt.codeforces;

 import java.io.BufferedInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.PushbackInputStream;
 import java.math.BigDecimal;
 import java.util.ArrayList;
 import java.util.List;

 /**
  * Created by Administrator on 2017/9/24.
  */
 public class HelgaHufflepuffsCup {

     final int LESS_THAN_K = 0;
     final int EQUAL_TO_K = 1;
     final int GREATER_THAN_K = 2;
     int n;
     int m;
     int k;
     int x;
     Node[] allNodes;
     long[][] backup;
     long modulo = 1000000000 + 7;
     int lessInitVal;
     int equalInitVal;
     int greaterInitVal;

     public static void main(String[] args) throws Exception {
         HelgaHufflepuffsCup solution = new HelgaHufflepuffsCup();
         solution.init();
         long result = solution.solve();
         System.out.println(result);
     }

     public void init() throws Exception {
         AcmInputReader input = new AcmInputReader(System.in);
         n = input.nextInteger();
         m = input.nextInteger();

         allNodes = new Node[n];
         for (int i = 0; i < n; i++) {
             allNodes[i] = new Node();
             allNodes[i].id = i + 1;
         }
         for (int i = 1; i < n; i++) {
             int n1 = input.nextInteger() - 1;
             int n2 = input.nextInteger() - 1;
             allNodes[n1].nearBy.add(allNodes[n2]);
             allNodes[n2].nearBy.add(allNodes[n1]);
         }

         k = input.nextInteger();
         x = input.nextInteger();
     }

     public long solve() {
         //Mark allNodes[0] as the root of tree
         backup = new long[3][x + 1];

         lessInitVal = k - 1;
         equalInitVal = 1;
         greaterInitVal = m - k;

         detect(allNodes[0], null);

         long sum = 0;
         for (int i = 0; i < 3; i++) {
             for (int j = 0; j <= x; j++) {
                 sum += allNodes[0].maybe[i][j];
             }
         }
         return sum % modulo;
     }

     public void detect(Node node, Node parent) {
         long[][] maybe = new long[3][x + 1];

         maybe[0][0] = lessInitVal;
         maybe[1][1] = equalInitVal;
         maybe[2][0] = greaterInitVal;

         for (Node nearby : node.nearBy) {
             if (nearby == parent) {
                 continue;
             }

             detect(nearby, node);
             combineInto(backup, maybe, nearby.maybe);
             {
                 long[][] tmp = backup;
                 backup = maybe;
                 maybe = tmp;
             }
         }

         node.maybe = maybe;
     }

     public void combineInto(long[][] result, long[][] father, long[][] kid) {
         for (int i = 0; i <= x; i++) {
             long result0i = 0;
             long result1i = 0;
             long result2i = 0;
             for (int j = 0; j <= i; j++) {
                 result0i += (father[0][i - j] * (kid[0][j] + kid[1][j] + kid[2][j])) % modulo;
                 result1i += (father[1][i - j] * kid[0][j]) % modulo;
                 result2i += (father[2][i - j] * (kid[0][j] + kid[2][j])) % modulo;
             }
             result[0][i] = result0i % modulo;
             result[1][i] = result1i % modulo;
             result[2][i] = result2i % modulo;
         }
     }

     static class Node {
         List<Node> nearBy = new ArrayList<>();
         long[][] maybe;
         int id;

         @Override
         public String toString() {
             StringBuilder sb = new StringBuilder();
             sb.append(id);
             sb.append("\n");
             if (maybe != null) {
                 for (int i = 0; i < 3; i++) {
                     for (int j = 0; j < maybe[i].length; j++) {
                         sb.append(maybe[i][j]);
                         sb.append(", ");
                     }
                     sb.setLength(sb.length() - 2);
                     sb.append("\n");
                 }
             }
             return sb.toString();
         }
     }

     /**
      * @author dalt
      * @see java.lang.AutoCloseable
      * @since java1.7
      */
     static class AcmInputReader implements AutoCloseable {
         private PushbackInputStream in;

         /**
          * 创建读取器
          *
          * @param input 输入流
          */
         public AcmInputReader(InputStream input) {
             in = new PushbackInputStream(new BufferedInputStream(input));
         }

         @Override
         public void close() throws IOException {
             in.close();
         }

         private int nextByte() throws IOException {
             return in.read() & 0xff;
         }

         /**
          * 如果下一个字节为b,则跳过该字节
          *
          * @param b 被跳过的字节值
          * @throws IOException if 输入流读取错误
          */
         public void skipByte(int b) throws IOException {
             int c;
             if ((c = nextByte()) != b) {
                 in.unread(c);
             }
         }

         /**
          * 如果后续k个字节均为b,则跳过k个字节。这里{@literal k<times}
          *
          * @param b     被跳过的字节值
          * @param times 跳过次数,-1表示无穷
          * @throws IOException if 输入流读取错误
          */
         public void skipByte(int b, int times) throws IOException {
             int c;
             while ((c = nextByte()) == b && times > 0) {
                 times--;
             }
             if (c != b) {
                 in.unread(c);
             }
         }

         /**
          * 类似于{@link #skipByte(int, int)}, 但是会跳过中间出现的空白字符。
          *
          * @param b     被跳过的字节值
          * @param times 跳过次数,-1表示无穷
          * @throws IOException if 输入流读取错误
          */
         public void skipBlankAndByte(int b, int times) throws IOException {
             int c;
             skipBlank();
             while ((c = nextByte()) == b && times > 0) {
                 times--;
                 skipBlank();
             }
             if (c != b) {
                 in.unread(c);
             }
         }

         /**
          * 读取下一块不含空白字符的字符块
          *
          * @return 下一块不含空白字符的字符块
          * @throws IOException if 输入流读取错误
          */
         public String nextBlock() throws IOException {
             skipBlank();
             StringBuilder sb = new StringBuilder();
             int c = nextByte();
             while (AsciiMarksLazyHolder.asciiMarks[c = nextByte()] != AsciiMarksLazyHolder.BLANK_MARK) {
                 sb.append((char) c);
             }
             in.unread(c);
             return sb.toString();
         }

         /**
          * 跳过输入流中后续空白字符
          *
          * @throws IOException if 输入流读取错误
          */
         private void skipBlank() throws IOException {
             int c;
             while ((c = nextByte()) <= 32) ;
             in.unread(c);
         }

         /**
          * 读取下一个整数(可正可负),这里没有对溢出做判断
          *
          * @return 下一个整数值
          * @throws IOException if 输入流读取错误
          */
         public int nextInteger() throws IOException {
             skipBlank();
             int value = 0;
             boolean positive = true;
             int c = nextByte();
             if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.SIGN_MARK) {
                 positive = c == '+';
             } else {
                 value = '0' - c;
             }
             c = nextByte();
             while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
                 value = (value << 3) + (value << 1) + '0' - c;
                 c = nextByte();
             }

             in.unread(c);
             return positive ? -value : value;
         }

         /**
          * 判断是否到了文件结尾
          *
          * @return true如果到了文件结尾,否则false
          * @throws IOException if 输入流读取错误
          */
         public boolean isMeetEOF() throws IOException {
             int c = nextByte();
             if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.EOF) {
                 return true;
             }
             in.unread(c);
             return false;
         }

         /**
          * 判断是否在跳过空白字符后抵达文件结尾
          *
          * @return true如果到了文件结尾,否则false
          * @throws IOException if 输入流读取错误
          */
         public boolean isMeetBlankAndEOF() throws IOException {
             skipBlank();
             int c = nextByte();
             if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.EOF) {
                 return true;
             }
             in.unread(c);
             return false;
         }

         /**
          * 获取下一个用英文字母组成的单词
          *
          * @return 下一个用英文字母组成的单词
          */
         public String nextWord() throws IOException {
             StringBuilder sb = new StringBuilder(16);
             skipBlank();
             int c;
             while ((AsciiMarksLazyHolder.asciiMarks[(c = nextByte())] & AsciiMarksLazyHolder.LETTER_MARK) != 0) {
                 sb.append((char) c);
             }
             in.unread(c);
             return sb.toString();
         }

         /**
          * 读取下一个长整数(可正可负),这里没有对溢出做判断
          *
          * @return 下一个长整数值
          * @throws IOException if 输入流读取错误
          */
         public long nextLong() throws IOException {
             skipBlank();
             long value = 0;
             boolean positive = true;
             int c = nextByte();
             if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.SIGN_MARK) {
                 positive = c == '+';
             } else {
                 value = '0' - c;
             }
             c = nextByte();
             while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
                 value = (value << 3) + (value << 1) + '0' - c;
                 c = nextByte();
             }
             in.unread(c);
             return positive ? -value : value;
         }

         /**
          * 读取下一个浮点数(可正可负),浮点数是近似值
          *
          * @return 下一个浮点数值
          * @throws IOException if 输入流读取错误
          */
         public float nextFloat() throws IOException {
             return (float) nextDouble();
         }

         /**
          * 读取下一个浮点数(可正可负),浮点数是近似值
          *
          * @return 下一个浮点数值
          * @throws IOException if 输入流读取错误
          */
         public double nextDouble() throws IOException {
             skipBlank();
             double value = 0;
             boolean positive = true;
             int c = nextByte();
             if (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.SIGN_MARK) {
                 positive = c == '+';
             } else {
                 value = c - '0';
             }
             c = nextByte();
             while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
                 value = value * 10.0 + c - '0';
                 c = nextByte();
             }

             if (c == '.') {
                 double littlePart = 0;
                 double base = 1;
                 c = nextByte();
                 while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
                     littlePart = littlePart * 10.0 + c - '0';
                     base *= 10.0;
                     c = nextByte();
                 }
                 value += littlePart / base;
             }
             in.unread(c);
             return positive ? value : -value;
         }

         /**
          * 读取下一个高精度数值
          *
          * @return 下一个高精度数值
          * @throws IOException if 输入流读取错误
          */
         public BigDecimal nextDecimal() throws IOException {
             skipBlank();
             StringBuilder sb = new StringBuilder();
             sb.append((char) nextByte());
             int c = nextByte();
             while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
                 sb.append((char) c);
                 c = nextByte();
             }
             if (c == '.') {
                 sb.append('.');
                 c = nextByte();
                 while (AsciiMarksLazyHolder.asciiMarks[c] == AsciiMarksLazyHolder.NUMERAL_MARK) {
                     sb.append((char) c);
                     c = nextByte();
                 }
             }
             in.unread(c);
             return new BigDecimal(sb.toString());
         }

         private static class AsciiMarksLazyHolder {
             public static final byte BLANK_MARK = 1;
             public static final byte SIGN_MARK = 1 << 1;
             public static final byte NUMERAL_MARK = 1 << 2;
             public static final byte UPPERCASE_LETTER_MARK = 1 << 3;
             public static final byte LOWERCASE_LETTER_MARK = 1 << 4;
             public static final byte LETTER_MARK = UPPERCASE_LETTER_MARK | LOWERCASE_LETTER_MARK;
             public static final byte EOF = 1 << 5;
             public static byte[] asciiMarks = new byte[256];

             static {
                 for (int i = 0; i <= 32; i++) {
                     asciiMarks[i] = BLANK_MARK;
                 }
                 asciiMarks['+'] = SIGN_MARK;
                 asciiMarks['-'] = SIGN_MARK;
                 for (int i = '0'; i <= '9'; i++) {
                     asciiMarks[i] = NUMERAL_MARK;
                 }
                 for (int i = 'a'; i <= 'z'; i++) {
                     asciiMarks[i] = LOWERCASE_LETTER_MARK;
                 }
                 for (int i = 'A'; i <= 'Z'; i++) {
                     asciiMarks[i] = UPPERCASE_LETTER_MARK;
                 }
                 asciiMarks[0xff] = EOF;
             }
         }
     }
 }

codeforces:Helga Hufflepuff's Cup的更多相关文章

  1. Helga Hufflepuff's Cup CodeForces - 855C

    Helga Hufflepuff's Cup CodeForces - 855C 题意:给一棵n个节点的树,要给每一个节点一个附加值,附加值可以为1-m中的一个整数.要求只能有最多x个节点有附加值k. ...

  2. Codeforces 855C - Helga Hufflepuff's Cup

    855C - Helga Hufflepuff's Cup 题意 要求构建一棵树,树上至多可以存在 \(x\) 个权值为 \(k\) 的重要点,且与重要点连边的点的权值必须小于 \(k\),问有多少种 ...

  3. C. Helga Hufflepuff's Cup 树形dp 难

    C. Helga Hufflepuff's Cup 这个题目我感觉挺难的,想了好久也写了很久,还是没有写出来. dp[i][j][k] 代表以 i 为根的子树中共选择了 j 个特殊颜色,且当前节点 i ...

  4. 【codeforces Manthan, Codefest 17 C】Helga Hufflepuff's Cup

    [链接]h在这里写链接 [题意]     k是最高级别的分数,最高界别的分数最多只能有x个.     1<=k<=m;     和k相邻的点的分数只能小于k;     n个点的树,问你每个 ...

  5. 【DP】【CF855C】 Helga Hufflepuff's Cup

    Description 给你一个树,可以染 \(m\) 个颜色,定义一个特殊颜色 \(k\) , 要求保证整棵树上特殊颜色的个数不超过 \(x\) 个.同时,如果一个节点是特殊颜色,那么它的相邻节点的 ...

  6. 855C Helga Hufflepuff's Cup

    传送门 题目大意 给你一棵树,可以染m种颜色,现定义一种特殊的颜色K,一棵树上最多能有x个特殊颜色.如果一个节点为特殊颜色,那么他相邻的节点的值只能选比K小的颜色,问一共有多少种染色方案. 分析 不难 ...

  7. Codeforces 855C. Helga Hufflepuff's Cup----树形DP

    z最近在学习树形DP...好难啊. 在cf上找到了一题c题当模版马克一下. 题目不贴了..>>http://codeforces.com/problemset/problem/855/C& ...

  8. Codeforces Round #351 (VK Cup 2016 Round 3, Div. 2 Edition) D Bear and Two Paths

    题目链接: http://codeforces.com/contest/673/problem/D 题意: 给四个不同点a,b,c,d,求是否能构造出两条哈密顿通路,一条a到b,一条c到d. 题解: ...

  9. Codeforces Round #351 (VK Cup 2016 Round 3, Div. 2 Edition) C - Bear and Colors

    题目链接: http://codeforces.com/contest/673/problem/C 题解: 枚举所有的区间,维护一下每种颜色出现的次数,记录一下出现最多且最小的就可以了. 暴力n*n. ...

随机推荐

  1. Android数据读取之Sqlite数据库操作

    咱们书接上文,继续来说说Android数据读取,这回,我们要讲的是Sqlite数据库的相关操作.以一个实例开始吧: 首先,上图,看看做成后的效果: 大概描述:类似于浏览器的收藏夹,网站名称,网站地址, ...

  2. P4简介:数据平面的编程语言

    15-05-29          http://p4.org/join-us/ 由两位SDN大师----来自普林斯顿的Jennifer Rexford和斯坦福的Nick McKeown---- 共同 ...

  3. 2018下C程序设计(上)第0次作业

    1.翻阅邹欣老师博客关于师生关系博客,并回答下列问题: (1)大学和高中最大的不同是什么?请看大学理想的师生关系是?有何感想? 我认为大学和高中最大的不同在于我们(包括老师)对学习的态度.在高中,学生 ...

  4. 使用js构造"ddMMMyy"格式的日期供postman使用(最low的方式)

    var date = new Date(); date.setDate(date.getDate() + 10); var year = date.getFullYear().toString().s ...

  5. mysql锁之Next-Key Locks

    一个Next-key锁结合了行锁和gap锁. InnoDB执行一个行级别锁在这样的一个途径,那就是它搜索或者扫描一个表索引时,它设置共享或者独占锁在它遭遇的索引记录上.于是,行级锁是真实的行记录锁.一 ...

  6. 互联网公司面试必问的mysql题目(下)

    这是mysql系列的下篇,上篇文章地址我附在文末. 什么是数据库索引?索引有哪几种类型?什么是最左前缀原则?索引算法有哪些?有什么区别? 索引是对数据库表中一列或多列的值进行排序的一种结构.一个非常恰 ...

  7. Android进程间的通信

    1.概述:由于android系统中应用程序之间不能共享内存.因此,在不同应用程序之间交互数据(跨进程通讯)就稍微麻烦一些.在android SDK中提供了4种用于跨进程通讯的方式.这4种方式正好对应于 ...

  8. LeetCode Degree of an Array

    原题链接在这里:https://leetcode.com/problems/degree-of-an-array/description/ 题目: Given a non-empty array of ...

  9. bisect模块用于插入

    参考链接: chttp://www.cnblogs.com/skydesign/archive/2011/09/02/2163592.html水

  10. Weblogic启动成功,控制台打不开

    有时候,我们在linux操作系统上成功启动了weblogic,也查看了7001端口的状态是开启的.但是访问weblogic控制台没有反应,也没有报错. 使用 netstat -ano | grep 7 ...