刷题总结——树有几多愁(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 分析: 斯坦纳树模板题. 简单来说,斯坦纳树问题就是给定一张有边权(或点权)的无向图,要求选若干条边使图中一些选定的点连通(可以经过其他点) ...
随机推荐
- Java 集合框架_下
Map接口 特点: [1]Map接口称为键值对集合或者映射集合,其中的元素(entry)是以键值对(key-value)的形式存在. [2]Map 容器接口中提供了增.删.改.查的方式对集合进行操作. ...
- python基础一 day14 生成器函数进阶(1)
- 2018.3.4 Linux and Unix 知识点
UNIX系统的特点 1.多任务 2.多用户 3.强大的网络功能 4.设备无关性 5.并行处理能力 6.开放性 7.错误处理 Linux系统的特点 1.自由软件 2.良好的兼容性 3.良好的界面 4.丰 ...
- 跑superpixel的程序
知乎上对superpixel的讲解还不错:https://www.zhihu.com/question/27623988 superpixel的算法有很多,opencv中也包含了很多,我找了一个比较经 ...
- WINDOWS-API:关于线程CreateThread,_beginthead(_beginthreadex),AfxBeginThread
[转]windows多线程编程CreateThread,_beginthead(_beginthreadex)和AfxBeginThread的区别 在Windows的多线程编程中,创建线程的函数主要有 ...
- PAT (Basic Level) Practise (中文)- 1008. 数组元素循环右移问题 (20)
一个数组A中存有N(N>0)个整数,在不允许使用另外数组的前提下,将每个整数循环向右移M(M>=0)个位置,即将A中的数据由(A0A1……AN-1)变换为(AN-M …… AN-1 A0 ...
- Bootstrap 提示工具(Tooltip)插件方法的用法
方法 下面是一些提示工具(Tooltip)插件中有用的方法: 方法 描述 实例 Options: .tooltip(options) 向元素集合附加提示工具句柄. $().tooltip(option ...
- C#基于联通短信Sgip协议构建短信网关程序
此软件基于中国联通Sgip协议程序接口,适合在中国联通申请了短信发送端口的公司使用.短信群发已经成为现在软件系统.网络营销等必不可少的应用工具.可应用在短信验证.信息群发.游戏虚拟商品购买.事件提醒. ...
- Java制作桌面弹球下载版 使用如鹏游戏引擎制作 包含2个精灵球同时弹动
package com.swift; import com.rupeng.game.GameCore; public class DesktopBouncingBall implements Runn ...
- 【思维题 线段树】cf446C. DZY Loves Fibonacci Numbers
我这种maintain写法好zz.考试时获得了40pts的RE好成绩 In mathematical terms, the sequence Fn of Fibonacci numbers is de ...