题面

解析

这题思路挺秒啊.

本麻瓜终于找了道好题了(还成功把ztlztl大仙拖下水了)

看到叶子节点数<=20就应该是状压啊.

然而DP要怎么写啊?

首先,考虑到编号肯定是从下往上一次增大的,

另外,对于没有分支的一条链,它的编号应该是连续的.

并且一种类似于贪心的想法就是一个点\(u\)被编号时它的子树一定被编号完了.

所以这也像是一个类似于拓扑序的东西.

先建一棵虚树(因为叶子节点只有20有很多没用的点),边权设为这条链上不在虚树上的点数.

设状态\(i\)表示状压后集合\(i\)中的点的编号已经确定了.

那么我们可以把所有已经编号了的点数\(cnt\)求出来,

然后枚举没在点集中的叶子节点,它的编号就应该是\(cnt+1\),再更新答案就行了.

因为取模后无法比较大小所以我们可以另外开一个\(double\)数组来比较.

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define int long long
#define fre(x) freopen(x".in","r",stdin),freopen(x".out","w",stdout)
using namespace std; inline int read(){
int sum=0,f=1;char ch=getchar();
while(ch>'9' || ch<'0'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0' && ch<='9'){sum=sum*10+ch-'0';ch=getchar();}
return f*sum;
} const int N=100005;
const int M=(1<<20)+5;
const int Mod=1000000007;
struct edge{int to,next,w;}e[N<<1];
struct node{int size,dep,fa,son,top,dfn,is;}a[N];
int n;
int head[N],cnt=0,tp;
int q[N],sta[N],tot,top;
int f[M],s[N];double dp[M];
int que[N],qq[N],tt; inline void add(int x,int y,int w){
e[++cnt]=(edge){head[x],y,w-1};head[x]=cnt;
} inline void dfs(int x,int fa){
a[x].dep++;a[x].fa=fa;
a[x].size=a[x].is=1;a[x].dfn=++tp;
for(int i=head[x];i;i=e[i].to){
int k=e[i].next;if(k==fa) continue;
a[k].dep=a[x].dep+e[i].w;
dfs(k,x);a[x].size+=e[i].w+a[k].size;
if(a[k].size>a[a[x].son].size) a[x].son=k;
a[x].is=0;
}
} inline void dfs2(int x,int top){
a[x].top=top;
if(a[x].son) dfs2(a[x].son,top);
for(int i=head[x];i;i=e[i].to){
int k=e[i].next;
if(k==a[x].son||k==a[x].fa) continue;
dfs2(k,k);
}
} inline int lca(int x,int y){
while(a[x].top!=a[y].top){
if(a[a[x].top].dep<a[a[y].top].dep) swap(x,y);
x=a[a[x].top].fa;
}
if(a[x].dep>a[y].dep) swap(x,y);
return x;
}//树剖求lca inline bool cmp(int x,int y){return a[x].dfn<a[y].dfn;} signed main(){
n=read();
for(int i=1;i<n;i++){int x=read(),y=read();add(x,y,1);add(y,x,1);}
dfs(1,0);dfs2(1,1);
for(int i=1;i<=n;i++) if(a[i].is) q[++tot]=i;
sort(q+1,q+tot+1,cmp);if(q[1]!=1) sta[++top]=1;
memset(head,0,sizeof(head));cnt=0;
//*******
for(int i=1;i<=tot;i++){
if(!top){sta[++top]=q[i];continue;}
int p=lca(sta[top],q[i]);
while(top>1&&a[sta[top-1]].dep>=a[p].dep)
{add(sta[top-1],sta[top],a[sta[top]].dep-a[sta[top-1]].dep);top--;}
if(sta[top]!=p) add(p,sta[top],a[sta[top]].dep-a[p].dep),sta[top]=p;
sta[++top]=q[i];
}
while(top>1) add(sta[top-1],sta[top],a[sta[top]].dep-a[sta[top-1]].dep),top--;
//*******建虚树
a[1].dep=0;dfs(1,0);int lim=1<<tot;
f[0]=dp[0]=1;
for(int i=0;i<lim;i++){
for(int j=1;j<=tot;j++) if((i&(1<<(j-1)))) s[q[j]]=1;
int ret=0,l=1,r=0;tt=0;
for(int j=1;j<=tot;j++) if(s[q[j]]) que[++r]=q[j];
while(l<=r){
int x=que[l];l++;qq[++tt]=x;
ret+=a[x].dep-a[a[x].fa].dep;
if(!a[x].fa) continue;qq[++tt]=a[x].fa;
s[a[x].fa]+=s[x]+a[x].dep-a[a[x].fa].dep-1;
if(s[a[x].fa]==a[a[x].fa].size-1) s[a[x].fa]++,que[++r]=a[x].fa;
}//像拓扑序一样统计数量
for(int i=1;i<=tt;i++) s[qq[i]]=0;//清空s数组(之前用memsetT得一脸懵逼)
ret++;
for(int j=1;j<=tot;j++){
if((i&(1<<(j-1)))) continue;
int k=i|(1<<(j-1));
if(dp[k]<dp[i]*ret) f[k]=f[i]*ret%Mod,dp[k]=dp[i]*ret;
}
}
printf("%lld\n",f[lim-1]);
return 0;
}

题解 [51nod1673] 树有几多愁的更多相关文章

  1. [51nod1673]树有几多愁

    lyk有一棵树,它想给这棵树重标号. 重标号后,这棵树的所有叶子节点的值为它到根的路径上的编号最小的点的编号. 这棵树的烦恼值为所有叶子节点的值的乘积. lyk想让这棵树的烦恼值最大,你只需输出最大烦 ...

  2. 51nod1673 树有几多愁 - 贪心策略 + 虚树 + 状压dp

    传送门 题目大意: 给一颗重新编号,叶子节点的值定义为他到根节点编号的最小值,求所有叶子节点值的乘积的最大值. 题目分析: 为什么我觉得这道题最难的是贪心啊..首先要想到 在一条链上,深度大的编号要小 ...

  3. 刷题总结——树有几多愁(51nod1673 虚树+状压dp+贪心)

    题目: lyk有一棵树,它想给这棵树重标号. 重标号后,这棵树的所有叶子节点的值为它到根的路径上的编号最小的点的编号. 这棵树的烦恼值为所有叶子节点的值的乘积. lyk想让这棵树的烦恼值最大,你只需输 ...

  4. 51nod 1673 树有几多愁

    lyk有一棵树,它想给这棵树重标号. 重标号后,这棵树的所有叶子节点的值为它到根的路径上的编号最小的点的编号. 这棵树的烦恼值为所有叶子节点的值的乘积. lyk想让这棵树的烦恼值最大,你只需输出最大烦 ...

  5. 51nod 1673 树有几多愁(链表维护树形DP+状压DP)

    题意 lyk有一棵树,它想给这棵树重标号. 重标号后,这棵树的所有叶子节点的值为它到根的路径上的编号最小的点的编号. 这棵树的烦恼值为所有叶子节点的值的乘积. lyk想让这棵树的烦恼值最大,你只需输出 ...

  6. 51nod 1673 树有几多愁——虚树+状压DP

    题目:http://www.51nod.com/Challenge/Problem.html#!#problemId=1673 建一个虚树. 一种贪心的想法是把较小的值填到叶子上,这样一个小值限制到的 ...

  7. POJ2182题解——线段树

    POJ2182题解——线段树 2019-12-20 by juruoOIer 1.线段树简介(来源:百度百科) 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线 ...

  8. Qtree3题解(树链剖分(伪)+线段树+set)

    外话:最近洛谷加了好多好题啊...原题入口 这题好像是SPOJ的题,挺不错的.看没有题解还是来一篇... 题意: 很明显吧.. 题解: 我的做法十分的暴力:树链剖分(伪)+线段树+\(set\)... ...

  9. Qtree3题解(树链剖分+线段树+set)

    外话:最近洛谷加了好多好题啊...原题入口 这题好像是SPOJ的题,挺不错的.看没有题解还是来一篇... 题意 很易懂吧.. 题解 我的做法十分的暴力:树链剖分(伪)+线段树+ std :: set ...

随机推荐

  1. [转帖]POWER ISA开源 浪潮商用机器加速POWER技术生态建设步伐

    POWER ISA开源 浪潮商用机器加速POWER技术生态建设步伐 [原创]   2019-08-26 18:51:04 关键字: 开源 Power 浪潮商用机器 http://server.zhid ...

  2. Spark Scala当中reduce的用法和例子

    [学习笔记] reduce将RDD中元素前两个传给输入函数,产生一个新的return值,将新产生的return值与RDD中下一个元素(即第三个元素)组成两个元素,再被传给输入函数,这样递归运作,直到最 ...

  3. Win7原装ISO镜像封装USB3.0&网卡驱动

    Win7原装ISO镜像封装USB3.0&网卡驱动   最新购买的电脑是Windows10系统,想装回Windows7,但是装Windows7发现网络适配器没出现,如果没有USB2.0接口,US ...

  4. ubuntu 18.04 LTS 安装ROS系统

    不同的ubuntu系统对应着不同的ROS版本,如果装错了就会提示 E:无法定位软件包 ROS有Melodic.Lunar.Kinetic不同的种类对应着不同的ubuntu版本 Melodic主要对应: ...

  5. k-近邻(KNN) 算法预测签到位置

    分类算法-k近邻算法(KNN): 定义: 如果一个样本在特征空间中的k个最相似 (即特征空间中最邻近) 的样本中的大多数属于某一个类别,则该样本也属于这个类别 来源: KNN算法最早是由Cover和H ...

  6. 简单使用setup.py来安装Python项目

    最近做个一个项目需要用到setup.py 这个构建工具来进行项目的便捷安装,把搜集到的一些资料加上个人理解整理成文章,如有错误的地方请各位大佬及时指出,小弟马上修改,下面正式进入setup.py的描述 ...

  7. 怎样启动和关闭nginx服务器

    启动: 直接使用命令: nginx nginx 关闭1: 快速停止 nginx -s stop 关闭2: 完整有序停止 nginx -s quit 重启: 如下 nginx -s reload

  8. POJ 1789 Prim

    给定N个字符串,某个字符串转为另一个字符串的花费为他们每一位不相同的字符数. 求最小花费Q. Input 多组输入,以0结束. 保证N不超过2000. Output 每组输出"The hig ...

  9. Jmeter4.0---- jmeter逻辑控制器(16)

    1.说明 逻辑控制器可以帮助用户控制Jmeter的测试逻辑,特别是何时发送请求.逻辑控制器可以改变其子测试元件的请求执行顺序. 2.逻辑控制器 (1)如果(if)控制器  用法一: 审核人员,数据分为 ...

  10. 作业1:java虚拟机内存模型图示

    看了很多篇文章,整理成一幅图,但仍然有许多不解的地方,以后再接着完善,哪位大神看到不正确的地方,请指出,谢谢.