刷题总结——树有几多愁(51nod1673 虚树+状压dp+贪心)
题目:
Input
第一行一个数n(1<=n<=100000)。
接下来n-1行,每行两个数ai,bi(1<=ai,bi<=n),表示存在一条边连接这两个点。
Output
一行表示答案
Input示例
5
1 2
2 4
2 3
3 5
Output示例
3
题解
挺神奇复杂的一道题···
首先是这道题的基本策略··容易想到贪心··我们尽量将小的数放在越靠近叶节点的地方···因为深度越小的点对所有叶节点的影响肯定是越大的····所以我们考虑每次先将一个节点子树填完后再填它本身··且它本身的编号一定是与子树中最大编号连续的···
另外由于只有20个叶节点··可以想到树上一定会有很多的链····对于链结合上面的策略我们可以相当链的编号一定是从链最下面的节点到上面严格递增的····因此我们可以将原树中的链全部省略掉···新建一个虚树··新的边为原来链的长度,由只有20个叶节点可以推出新的树的节点数不会多于100个··大大减少复杂度··
最后由20个叶节点可以想到状压dp····这是本题中最为复杂的地方···的
我们用f[i]表示我们选取了i状态叶节点下的乘积的最小值···首先由i状态我们可以确定有哪些节点的所在子树的叶节点是全部取到了的···由此该节点u所在子树到它在新树中的父亲节点的编号肯定是确定的(由基本策略可以推出编号肯定是已经填到了1——size[u]+len[u],size[u]为u所在子树大小,len[u]为它到它在新树中的父亲节点的边的长度)
设上面的范围为(1——t),那么接下来要填的叶子节点的编号肯定是t+1,此时我们只需枚举接下来要填的叶子节点是哪一个···然后用f[i]*(t+1)的值去更新f[i|(x)]即可,其中x为我们枚举的那一个叶子····
另外注意本题是要取模的··但为了在dp时比较大小我们需要准备两个dp数组··一个用于记录正确的取了模的答案··一个用于比较··比较的那个数组可以用log或者用double来比较(double的范围是很大的····1.7*10(308))
代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
using namespace std;
const int N=1e5+;
const int mod=1e9+;
int n,first[N],go[N*],next[N*],tot,cntlf,lf[],size[],cnt,lim,got[],len[];
long long dp[<<];
double f[<<];
bool islf[N],del[N];
vector<int>g[];
inline int R()
{
char c;int f=;
for(c=getchar();c<''||c>'';c=getchar());
for(;c<=''&&c>='';c=getchar()) f=(f<<)+(f<<)+c-'';
return f;
}
inline void comb(int a,int b)
{
next[++tot]=first[a],first[a]=tot,go[tot]=b;
next[++tot]=first[b],first[b]=tot,go[tot]=a;
}
inline void dfs1(int u,int fa)
{
int sum=;
for(int e=first[u];e;e=next[e])
{
int v=go[e];if(v==fa) continue;
sum++;dfs1(v,u);
}
if(!sum) islf[u]=true;
else if(sum==) del[u]=true;
}
inline void dfs2(int u,int fa,int tempfa,int templen)
{
templen++;
if(!del[u])
{
len[++cnt]=templen;templen=;
if(islf[u]) lf[++cntlf]=cnt;
if(tempfa) g[tempfa].push_back(cnt);
tempfa=cnt;
}
for(int e=first[u];e;e=next[e])
{
int v=go[e];if(v==fa) continue;
dfs2(v,u,tempfa,templen);
}
}
int main()
{
//freopen("a.in","r",stdin);
n=R();int a,b;
for(int i=;i<n;i++) a=R(),b=R(),comb(a,b);
dfs1(,);dfs2(,,,);
for(int i=;i<=cntlf;i++) size[lf[i]]=;
for(int i=cnt;i>=;i--)
for(int j=;j<g[i].size();j++) size[i]+=size[g[i][j]];
lim=(<<cntlf);dp[]=f[]=;
for(int i=;i<lim;i++)
{
int num=;
memset(got,,sizeof(got));
for(int j=;j<=cntlf;j++)
if(i&(<<(j-))) got[lf[j]]=;
for(int j=cnt;j>=;j--)
{
for(int k=;k<g[j].size();k++) got[j]+=got[g[j][k]];
if(got[j]==size[j]) num+=len[j];
}
double temp=f[i]*num;
for(int j=;j<=cntlf;j++)
if(!(i&(<<(j-)))&&temp>f[i|(<<(j-))])
f[i|(<<(j-))]=temp,dp[i|(<<(j-))]=(long long)dp[i]*num%mod;
}
cout<<dp[lim-]<<endl;
return ;
}
刷题总结——树有几多愁(51nod1673 虚树+状压dp+贪心)的更多相关文章
- 51nod1673 树有几多愁 - 贪心策略 + 虚树 + 状压dp
传送门 题目大意: 给一颗重新编号,叶子节点的值定义为他到根节点编号的最小值,求所有叶子节点值的乘积的最大值. 题目分析: 为什么我觉得这道题最难的是贪心啊..首先要想到 在一条链上,深度大的编号要小 ...
- 51nod 1673 树有几多愁——虚树+状压DP
题目:http://www.51nod.com/Challenge/Problem.html#!#problemId=1673 建一个虚树. 一种贪心的想法是把较小的值填到叶子上,这样一个小值限制到的 ...
- 刷题向》关于第一篇状压DP BZOJ1087 (EASY+)
这是本蒟蒻做的第一篇状压DP,有纪念意义. 这道题题目对状压DP十分友善,算是一道模板题. 分析题目,我们发现可以用0和1代表每一个格子的国王情况, 题目所说国王不能相邻放置,那么首先对于每一行是否合 ...
- 刷题总结——bzoj1725(状压dp)
题目: 题目描述 Farmer John 新买了一块长方形的牧场,这块牧场被划分成 N 行 M 列(1<=M<=12; 1<=N<=12),每一格都是一块正方形的土地. FJ ...
- 【62测试】【状压dp】【dfs序】【线段树】
第一题: 给出一个长度不超过100只包含'B'和'R'的字符串,将其无限重复下去. 比如,BBRB则会形成 BBRBBBRBBBRB 现在给出一个区间[l,r]询问该区间内有多少个字符'B'(区间下标 ...
- luogu4294 [WC2008]游览计划(状压DP/斯坦纳树)
link 题目大意:给定一个网格图,有些点是关键点,选择格点有代价,求把所有关键点联通的最小代价 斯坦纳树模板题 斯坦纳树问题:给定一个图结构,有一些点是关键点,求把这些关键点联通的最小代价e 斯坦纳 ...
- 【思维题 状压dp】APC001F - XOR Tree
可能算是道中规中矩的套路题吧…… Time limit : 2sec / Memory limit : 256MB Problem Statement You are given a tree wit ...
- [bzoj4006][JLOI2015]管道连接_斯坦纳树_状压dp
管道连接 bzoj-4006 JLOI-2015 题目大意:给定一张$n$个节点$m$条边的带边权无向图.并且给定$p$个重要节点,每个重要节点都有一个颜色.求一个边权和最小的边集使得颜色相同的重要节 ...
- 【BZOJ2595_洛谷4294】[WC2008]游览计划(斯坦纳树_状压DP)
上个月写的题qwq--突然想写篇博客 题目: 洛谷4294 分析: 斯坦纳树模板题. 简单来说,斯坦纳树问题就是给定一张有边权(或点权)的无向图,要求选若干条边使图中一些选定的点连通(可以经过其他点) ...
随机推荐
- 使用ImageList组件制作动画图片
实现效果: 知识运用: Timer组件的Enabled属性 Tick事件 PictureBox控件的Image属性 ImageList组件的Images属性 实现代码: private void F ...
- python判断平衡二叉树
题目:输入一棵二叉树,判断该二叉树是否是平衡二叉树.若左右子树深度差不超过1则为一颗平衡二叉树. 思路: 使用获取二叉树深度的方法来获取左右子树的深度 左右深度相减,若大于1返回False 通过递归对 ...
- C10 C语言数据结构
目录 枚举 结构体 共用体 枚举 enum enum枚举是 C 语言中的一种基本数据类型,它可以让数据更简洁,更易读. 枚举语法定义格式为: enum 枚举名 {枚举元素1,枚举元素2,……}; 枚举 ...
- LiteIDE 错误: 进程无法启动
问题 运行 01_hello.go,提示以下错误 新建文件夹().exe [C:/Users/Administrator/Desktop/新建文件夹()] 错误: 进程无法启动. 原因 工程目录名不能 ...
- Java第11次作业:什么是继承?继承的好处?什么是覆写?super()?构造代码块?子父类初始化顺序? 抽象类能用final声明吗?final关键字声明类 方法 变量以及全局常量?抽象类的构造方法?
什么是继承? 继承是以父类为基础,子类可以增加新的数据或新的功能.子类不能选择性地继承父类.这种技术使得复用以前的代码非常容易. JAVA不支持多继承,单继承使JAVA的继承关系很简单,一个类只能有一 ...
- [转]LLE
原始特征的数量可能很大,或者说样本是处于一个高维空间中,通过映射或变换的方法,降高维数据降低到低维空间中的数据,这个过程叫特征提取,也称降维. 特征提取得基本任务研究从众多特征中求出那些对分类最有效的 ...
- MySQL 查询优化之 Block Nested-Loop 与 Batched Key Access Joins
MySQL 查询优化之 Block Nested-Loop 与 Batched Key Access Joins 在MySQL中,可以使用批量密钥访问(BKA)连接算法,该算法使用对连接表的索引访问和 ...
- Docker 镜像&仓库 获取及推送镜像
docker查看.删除镜像 docker镜像存储位置: /var/lib/docker 查看docker信息也可以查看保存位置 docker info 1.列出镜像 docker images -aa ...
- 根据参数优化nginx的服务性能
一.优化nginx服务的worker进程数 在高并发.高访问量的Web服务场景,需要事先启动好更多的nginx进程,以保证快速响应并处理大量并发用户的请求. 1).优化nginx进程对应的配置 优化n ...
- mysql查询的语法
单表查询语法 SELECT DISTINCT 字段1,字段2... FROM 表名 WHERE 条件 GROUP BY field HAVING 筛选 ORDER BY field LIMIT 限制条 ...