NOIP模拟(10.22)T3 树
来源:互联网 发布:tv软件市场 编辑:程序博客网 时间:2024/05/22 17:41
题目背景:
10.22 NOIP模拟作业T3
分析:状压DP
首先证明一些东西。
1、显然,编号越小的点深度越深越好
证明:显然,如果一个点的编号很小,它的深度越高,意味着它影响的叶子节点越多,那么显然就会很不优。
2、对于以一个点为根的子树,它的编号一定是连续的。
证明:假设对于一个点x它子树的size为y,但是编号为1 ~ y - 1,和y + 1,那么必然有另一个地方的编号为y,因为我们已经说过,如果,将编号y放在x当中,因为y为整个子树当中的最大编号,那么它不会影响x子树中叶子节点的路径最小值,但是将y放在其他地方因为它是目前最小的编号,所以一定会影响一些叶子节点的取值,那么显然是不优秀的,所以对于一个子树它的编号一定是连续的。
说到这里想必思路就比较清晰了,我们的编号策略就是,从小到大编号,从叶子节点标起,一个非叶结点被标记,当且仅当,它的整个子树均被标记,那么现在影响答案的因素就只有一个了,即叶子节点的标号顺序,我们可以用f[i]状压表示,当前标记了哪些叶子节点,这些叶子节点的最优答案是多少。因为对于每一个状态我们都需要dfs判定,当前被确定了哪些编号,那么复杂度是O(2 ^ 叶子节点数 * 树的大小)这样显然复杂度偏高,考虑该怎么优化一下,我们发现,对于一个非叶非根节点,如果它只有一个儿子,那么它是可以和它的儿子放在一起的,儿子一旦标记,它就需要标记,那么我们可以在一开始的时候,把这种情况的节点,也就是一条链缩到一起,这样树的大小就很小了,(最大的情况为满二叉树),可以搞定。
具体实现:
g[i]表示,在状态i中存在的叶子节点全部标记之后,这i个权值之积最大为g[i],枚举到i状态时,dfs找寻当前能够被确定的已经标记的节点有sum个,那么下一个被标记的叶子节点的值一定为sum + 1(上面已经证明过了),所以我们可以将g[i] à g[i | (1<< j)] ((i & (1 << j)) == 0)然后最后的g[i]就是答案啦。
注意:因为答案过大需要取模,所以,我们可以存储两个值g[i],f[i],g[i]为答案本身的值,用double存储,来用于比较,f[i]为int即取好模的答案。
Source:
/*created by scarlyw*/#include <iostream>#include <cstdio>#include <string>#include <cstring>#include <cmath>#include <algorithm>#include <cctype>#include <set>#include <map>#include <vector>#include <queue>const int MAXX = 20;const int MAXN = 100000 + 10;const int mod = 1000000000 + 7;int n, x, y, sum;std::vector<int> leaves;std::vector<int> ori_edge[MAXN], edge[MAXN];int f[1 << MAXX], dep[MAXN], num[MAXN], size[MAXN];double g[1 << MAXX];bool use[MAXN];inline void add_ori_edge(int x, int y) {ori_edge[x].push_back(y), ori_edge[y].push_back(x);}inline void read_in() {scanf("%d", &n);for (int i = 1; i < n; ++i) scanf("%d%d", &x, &y), add_ori_edge(x, y);}inline void get_point(int cur, int fa) {/*标记有意义的节点*/dep[cur] = dep[fa] + 1, size[cur] = 1;int cnt = 0;for (int p = 0; p < ori_edge[cur].size(); ++p) {int v = ori_edge[cur][p];if (v != fa) get_point(v, cur), size[cur] += size[v], ++cnt;}if (cnt != 1) use[cur] = true;if (cnt == 0) leaves.push_back(cur);}inline void build_new_tree(int cur, int fa, int last) {/*建立新的树*/ if (use[cur] && last) edge[last].push_back(cur);if (use[cur]) last = cur;for (int p = 0; p < ori_edge[cur].size(); ++p) {int v = ori_edge[cur][p];if (v != fa) build_new_tree(v, cur, last);}}inline void dfs(int cur, int fa) {/*查找多少个点的标号被确定*/ if (edge[cur].size() == 0) return (void)(sum += num[cur]);num[cur] = 0;for (int p = 0; p < edge[cur].size(); ++p) {int v = edge[cur][p];if (v != fa) {dfs(v, cur);if (num[v] == size[v]) {num[cur] += num[v] + dep[v] - dep[cur] - 1;sum += dep[v] - dep[cur] - 1;}}}if (num[cur] == size[cur] - 1) num[cur]++, sum++;}inline void solve() {use[1] = true, get_point(1, 0), build_new_tree(1, 0, 0);for (int i = 0; i < leaves.size(); ++i) f[1 << i] = g[1 << i] = 1; for (int i = 0, s = (1 << int(leaves.size())); i < s; ++i) {for (int j = 0; j < leaves.size(); ++j) num[leaves[j]] = (i & (1 << j)) ? 1 : 0;sum = 0, dfs(1, 0);double max = g[i] * (double)(sum + 1);int ans = (long long)f[i] * (long long)(sum + 1) % mod;for (int j = 0; j < leaves.size(); ++j)if ((i & (1 << j)) == 0 && g[i | (1 << j)] < max)g[i | (1 << j)] = max, f[i | (1 << j)] = ans; }std::cout << f[(1 << int(leaves.size())) - 1];}int main() {read_in();solve();return 0;}
- NOIP模拟(10.22)T3 树
- 【NOIP 模拟题】[T3] 约会(lca)
- NOIP模拟(10.19)T3 放盒子
- NOIP模拟(10.20)T3 裁剪表格
- NOIP模拟(20171023)T3 拆网线
- NOIP模拟(10.23)T3 拆网线
- NOIP模拟(10.24)T3 Math
- NOIP模拟(20171024)T3 数学
- NOIP模拟(20171026)T3 大逃杀
- NOIP模拟(10.26)T3 大逃杀
- NOIP模拟(10.27)T3 心灵治愈
- NOIP模拟(10.30)T3 星星
- NOIP模拟(10.31)T3 纸带
- NOIP模拟(20171030)T3 星星
- NOIP模拟(11.07)T3 图
- NOIP模拟(20171031)T3 纸带
- noip模拟11.3 T3
- 【20160904】NOIP模拟赛T3
- 在 Intellij IDEA中设置Maven 路径及配置文件的方法(使用阿里云Maven源)
- java控制台中文汉字乱码
- 用 Doc2Vec 得到文档/段落/句子的向量表达
- Python-字典-通讯录
- 黑科技世界–随身携带的小空调Aircon Watch
- NOIP模拟(10.22)T3 树
- CCPC 2017 哈尔滨赛区现场赛 比赛总结
- engg
- 球员列表 查询 +排序+新增球员+敏感字符
- 【二分+Two Pointers】51Nod 1686 第K大区间
- [枚举 线段树] 51Nod1494 选举拉票
- 51nod 1383 整数分解为2的幂【递推】
- [P2680][NOIP2015]运输计划
- 查找mysql数据库文件位置