刷题总结——树有几多愁(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 分析: 斯坦纳树模板题. 简单来说,斯坦纳树问题就是给定一张有边权(或点权)的无向图,要求选若干条边使图中一些选定的点连通(可以经过其他点) ...
随机推荐
- CMDB资产采集方案
CMDB资产采集方案 CMDB 资产采集的方案总共有四种 Agent SSH类 Saltstack Puttet 方案设计,从性能上考虑 下面前三种是用Python开发的,目标是兼容三种采集方式的软件 ...
- 天坑之mysql乱码问题以及mysql重启出现1067的错误解决
相信很多小伙伴都遇到过数据库中文乱码问题,很头疼,明明Navicat上的编码格式都是utf-8是一样的啊? 为什么还是乱码? 原因是Navicat上的数据库编码格式并不是真正的编码格式 ,所以明白了吗 ...
- Dojo的dojoConfig函数
在我们引入 Dojo 的时候都会先做一些全局的配置,所使用的就是 Dojo 的 Config 接口. dojoConfig为以前的dgConfig函数. <script type="t ...
- bash编程之case语句,函数
bash脚本编程:之case语句 条件测试: 0: 成功 1-255: 失败 命令: [ expression ] [[ expression ]] test expression exP ...
- HTML与XHTML区别
1. html超文本标记语言,xhtml可扩展超文本标记语言,xhtml是将html作为xml的应用重新定义的一个标准. 2. xhtm比html的代码规则严格很多,例如'a < b'在xhtm ...
- scipy学习之(二)——io操作及其misc操作对图片的处理
SciPy有许多模块.类和函数,io子模块可用于从各种文件格式中读取数据和将数据写入各种文件格式. from scipy import io import numpy as np 生成数据 data ...
- Linux - mkdir -p a/b/c
mkdir -p a/b/c -p 会循环创建
- MySQL左右连接查询中的NULL的数据筛选问题
这里使用左连接为例子,对于左连接是将左边表的数据显示,右边表中如果没有对应的数据则使用null填充. game表: game_type表: SELECT g.name,g.type_id,t.type ...
- FastJsonUtils工具类
fastjson是由alibaba开源的一套json处理器.与其他json处理器(如Gson,Jackson等)和其他的Java对象序列化反序列化方式相比,有比较明显的性能优势. 版权声明:本文为博主 ...
- linux下GPIO的用户层操作(sysfs)
linux的GPIO通过sysfs为用户提供服务,下面是linux kernel里的说明文档,学习一下. GPIO Sysfs Interface for Userspace ============ ...