51nod1673 树有几多愁 - 贪心策略 + 虚树 + 状压dp
题目大意:
给一颗重新编号,叶子节点的值定义为他到根节点编号的最小值,求所有叶子节点值的乘积的最大值。
题目分析:
为什么我觉得这道题最难的是贪心啊。。首先要想到
- 在一条链上,深度大的编号要小于深度小的编号(保证它影响的节点是最小的)
- 有了1过后,一颗子树的编号应该是以叶子节点为最小的连续整数,也就是说必须对一个节点的所有子树编完号才能对该节点编号。
这两点我已经想了很久了,接下来还有难关:
知道了叶子节点编号要最小,但是叶子节点的编号顺序会对答案产生巨大影响。注意到题目中保证叶子节点数\(\le 20\),-------->状压,用\(f[i]\)表示染完i这个状态的叶子所得到的最优答案(取模),由于中途转移时会产生巨大的中间量,为了避免使用高精度,再新建一个\(g[i]\)表示最优答案(未取模)。这样当枚举到某一状态时,计算出下一个叶子节点的编号应该是多少,并进行转移。
还没完,计算下一个叶子节点的编号需要对树进行一次遍历,由于n巨大,如果对原树进行遍历的话,总时间复杂度会达到\(O(2^{20}*n)\),这就远超出了范围。有前面得知一条链上的编号是连续的,那么就是说只要知道链的长度就可以知道编号的增量,也就是说我们只用保留叶节点、根节点、包含多颗子树的节点这些关键点,这不就是颗虚树吗?
建完虚树后,进行如上转移,即可得到答案。
code
#include<bits/stdc++.h>
using namespace std;
#define maxn 100050
#define limit (1<<21)
const int mod = 1e9 + 7;
namespace IO{
inline int read(){
int i = 0, f = 1; char ch = getchar();
for(; (ch < '0' || ch > '9') && ch != '-'; ch = getchar());
if(ch == '-') f = -1, ch = getchar();
for(; ch >= '0' && ch <= '9'; ch = getchar()) i = (i << 3) + (i << 1) + (ch - '0');
return i * f;
}
inline void wr(int x){
if(x < 0) x = -x, putchar('-');
if(x > 9) wr(x / 10);
putchar(x % 10 + '0');
}
}using namespace IO;
int n;
int ecnt, adj[maxn], go[maxn*2], nxt[maxn*2], fa[maxn][25];
int dep[maxn], sze[maxn], num[maxn], leaf[maxn], tot;
int vir[maxn], virCnt, vecnt, vadj[maxn], vgo[maxn], vnxt[maxn], par[maxn], rt;
int dfn[maxn], clk, sum;
int f[limit];
double g[limit];
inline void addEdge(int u, int v){
nxt[++ecnt] = adj[u], adj[u] = ecnt, go[ecnt] = v;
}
inline void addvEdge(int u, int v){
vnxt[++vecnt] = vadj[u], vadj[u] = vecnt, vgo[vecnt] = v;
}
inline void pre(int u, int f){
// cout<<u<<"->";
dep[u] = dep[f] + 1;
dfn[u] = ++clk;
fa[u][0] = f;
sze[u] = 1;
for(int i = 1; i <= 20; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1];
int cnt = 0;
for(int e = adj[u]; e; e = nxt[e]){
int v = go[e];
if(v == f) continue;
pre(v, u);
sze[u] += sze[v];
cnt++;
}
if(u == 1) return;
if(cnt == 0) leaf[tot++] = u, vir[++virCnt] = u;
else if(cnt >= 2) vir[++virCnt] = u;
}
inline int getLca(int u, int v){
if(dep[u] < dep[v]) swap(u, v);
int delta = dep[u] - dep[v];
for(int i = 20; i >= 0; i--) if(delta & (1 << i)) u = fa[u][i];
if(u == v) return u;
for(int i = 20; i >= 0; i--)
if(fa[u][i] != fa[v][i]) u = fa[u][i], v = fa[v][i];
return fa[u][0];
}
inline bool cmp(int u, int v){
return dfn[u] < dfn[v];
}
inline int buildVir(){
static int stk[maxn], top;
top = 0;
sort(vir + 1, vir + virCnt + 1, cmp);
int oriSze = virCnt;
for(int i = 1; i <= oriSze; i++){
int u = vir[i];
if(!top){
stk[++top] = u;
par[u] = 0;
continue;
}
int lca = getLca(u, stk[top]);
// cout<<u<<" "<<stk[top]<<" "<<lca<<endl;
while(dep[lca] < dep[stk[top]]){
if(dep[stk[top - 1]] < dep[lca]) par[stk[top]] = lca;
top--;
}
if(lca != stk[top]){
vir[++virCnt] = lca;
stk[++top] = lca;
par[lca] = stk[top];
}
par[u] = lca;
stk[++top] = u;
}
sort(vir + 1, vir + virCnt + 1, cmp);
for(int i = 1; i <= virCnt; i++)
if(par[vir[i]]) addvEdge(par[vir[i]], vir[i]);
return vir[1];
}
inline void number(int u){
if(!vadj[u]){
sum += num[u];
return;
}
num[u] = 0;
for(int e = vadj[u]; e; e = vnxt[e]){
int v = vgo[e];
number(v);
if(num[v] == sze[v]){ //该子树已经全部染完
num[u] += num[v] + dep[v] - dep[u] - 1;
sum += dep[v] - dep[u] - 1; //更新已经染到的编号
}
}
if(num[u] == sze[u] - 1){ //该根的子树已经全部染完
num[u]++, sum++;
}
}
inline void print(int t){
cout<<t<<"->";
for(int e = vadj[t]; e; e = nxt[e]){
int v = vgo[e];
print(v);
}
}
int main(){
n = read();
for(int i = 1; i < n; i++){
int x = read(), y = read();
addEdge(x, y), addEdge(y, x);
}
vir[virCnt = 1] = 1;
pre(1, 0);
rt = buildVir();
for(int i = 0; i < tot; i++) f[1 << i] = 1, g[1 << i] = 1.0;
int limi = (1 << tot);
for(int i = 1; i < limi; i++){
for(int j = 0; j < tot; j++){
if(i & (1 << j)) num[leaf[j]] = 1;
else num[leaf[j]] = 0;
}
sum = 0;
number(rt); //得到下一个叶子节点的编号
double ret = g[i] * (sum + 1); //用double来做中间的比较,避免高精度
int ans = 1ll * f[i] * (sum + 1) % mod;
for(int j = 0; j < tot; j++){
if(((i & (1 << j)) == 0) && g[i | (1 << j)] < ret){
g[i | (1 << j)] = ret;
f[i | (1 << j)] = ans;
}
}
}
wr(f[limi - 1]);
return 0;
}
51nod1673 树有几多愁 - 贪心策略 + 虚树 + 状压dp的更多相关文章
- 【BZOJ2595_洛谷4294】[WC2008]游览计划(斯坦纳树_状压DP)
上个月写的题qwq--突然想写篇博客 题目: 洛谷4294 分析: 斯坦纳树模板题. 简单来说,斯坦纳树问题就是给定一张有边权(或点权)的无向图,要求选若干条边使图中一些选定的点连通(可以经过其他点) ...
- 刷题总结——树有几多愁(51nod1673 虚树+状压dp+贪心)
题目: lyk有一棵树,它想给这棵树重标号. 重标号后,这棵树的所有叶子节点的值为它到根的路径上的编号最小的点的编号. 这棵树的烦恼值为所有叶子节点的值的乘积. lyk想让这棵树的烦恼值最大,你只需输 ...
- 51nod 1673 树有几多愁——虚树+状压DP
题目:http://www.51nod.com/Challenge/Problem.html#!#problemId=1673 建一个虚树. 一种贪心的想法是把较小的值填到叶子上,这样一个小值限制到的 ...
- Codeforces 429C Guess the Tree(状压DP+贪心)
吐槽:这道题真心坑...做了一整天,我太蒻了... 题意 构造一棵 $ n $ 个节点的树,要求满足以下条件: 每个非叶子节点至少包含2个儿子: 以节点 $ i $ 为根的子树中必须包含 $ c_i ...
- bzoj3717 [PA2014]Pakowanie 贪心+状压DP
题目传送门 https://lydsy.com/JudgeOnline/problem.php?id=3717 题解 这道题大概也就只能算常规的状压 DP 吧,但是这个状态和转移的设计还是不是很好想. ...
- 【62测试】【状压dp】【dfs序】【线段树】
第一题: 给出一个长度不超过100只包含'B'和'R'的字符串,将其无限重复下去. 比如,BBRB则会形成 BBRBBBRBBBRB 现在给出一个区间[l,r]询问该区间内有多少个字符'B'(区间下标 ...
- bzoj 4006 [JLOI2015]管道连接(斯坦纳树+状压DP)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=4006 [题意] 给定n点m边的图,连接边(u,v)需要花费w,问满足使k个点中同颜色的 ...
- 2018.08.29 NOIP模拟 movie(状压dp/随机化贪心)
[描述] 小石头喜欢看电影,选择有 N 部电影可供选择,每一部电影会在一天的不同时段播 放.他希望连续看 L 分钟的电影.因为电影院是他家开的,所以他可以在一部电影播放过程中任何时间进入或退出,当然他 ...
- bzoj1402 Ticket to Ride 斯坦纳树 + 状压dp
给定\(n\)个点,\(m\)条边的带权无向图 选出一些边,使得\(4\)对点之间可达,询问权值最小为多少 \(n \leqslant 30, m \leqslant 1000\) 首先看数据范围,\ ...
随机推荐
- 如何将String类型转换成任意基本类型
[原创][C#] 如何将String类型转换成任意基本类型 Posted on 2009-12-02 09:47 YCOE 阅读( 2843) 评论( 14) 编辑 收藏 前几天,在写一个自动 ...
- 11.Cocos2dx2.2下使用JNI技术调用jar包里面的一些方法遇到的一些问题及解决方式。
<span style="font-family: Arial, Helvetica, sans-serif;">步骤一:导入JniHelper.h头文件.</s ...
- Webpack学习手册
多端阅读<Webpack官方文档>: 在PC/MAC上查看:下载w3cschool客户端,进入客户端后通过搜索当前教程手册的名称并下载,就可以查看当前离线教程文档.下载Webpack官方文 ...
- JNI各种环境下编译方法及初期出错分析
转自 https://www.cnblogs.com/xyang0917/p/4172490.html 第五步.将C/C++代码编译成本地动态库文件 动态库文件名命名规则:lib+动态库文件名+后缀( ...
- Log Explorer 使用说明(原创)
关于Log Explorer (我抄的) 介绍Log Explorer主要用于对MSSQLServer的事物分析和数据恢复.你可以浏览日志.导出数据.恢复被修改或者删除的数据(包括执行过update, ...
- MySQL—Install/Remove of the Service Denied
在Windos7下通过命令"mysqld --install"安装MySQL数据库时出现了"Install/Remove of the Service Denied&qu ...
- 用Java将字符串的首字母转换大小写
在项目开发的时候会需要统一字符串的格式,比如首字母要求统一大写或小写,那用Java如何实现这一功能?下面一起来学习学习. 话不多说,直接上代码 ? 1 2 3 4 5 6 7 8 9 10 11 12 ...
- api接口安全以及https
一:加密方法: 1,对称加密 AES,3DES,DES等,适合做大量数据或数据文件的加解密. 2,非对称加密 如RSA,Rabin.公钥加密,私钥解密.对大数据量进行加解密时性能较低. 二:https ...
- 谈谈我对P2P网络借贷的一些看法
北漂期间,只知道互联网金融非常火,相关创业公司和项目也非常多.2013年,最火的是余额宝等宝宝类产品.当时的收益率达到了7%,流动性如此高的情况下,竟达到这么高的收益率,我简直不敢相信.另外,当时考虑 ...
- Swift3.0为视图添加旋转动画_CABasicAnimation
Swift2.3: //创建旋转动画 let anim = CABasicAnimation(keyPath: "transform.rotation") //旋转角度 anim. ...