51nod 1673 树有几多愁

lyk有一棵树,它想给这棵树重标号。

重标号后,这棵树的所有叶子节点的值为它到根的路径上的编号最小的点的编号。

这棵树的烦恼值为所有叶子节点的值的乘积。

lyk想让这棵树的烦恼值最大,你只需输出最大烦恼值对1e9+7取模后的值就可以了。

注意一开始1号节点为根,重标号后这个节点仍然为根。

update:数据保证叶子节点个数<=20。

例如样例中,将1,2,3,4,5重标号为4,3,1,5,2,此时原来编号为4,5的两个叶子节点的值为3与1,这棵树的烦恼值为3。不存在其它更优解。

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

有一个贪心,就是把小的数尽可能放到靠近叶子节点的点,即从叶子到根逐渐变大

这样小的值就影响尽可能少的叶子

先可以把儿子数为1的点缩掉,这样缩完点后的新树只有40个点左右

我们设$f[S]$表示叶子状态为S的最大烦恼值

为1表示已经分配了值

现在我们要给i分配一个值,转移到S|(1<<i-1)

我们为了满足贪心原则,只有满足子树中叶子全部出现在了S中才有值

否则就会出现矛盾,即从i叶子到根会有比i小的情况

也就是算出这些子树的节点数id,然后叶子i填id+1,计入答案

由于取模不能判大小,所以在维护一个double比大小

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 #include<cmath>
 6 using namespace std;
 7 typedef long long lol;
 8 struct Node
 9 {
10   int next,to;
11 }edge[200001],edge2[200001];
12 int head[100001],num,num2,head2[100001],dep[100001],in[100001],b[51],cnt,tot,n,l[51],sz[100001],s[100001];
13 double f[1<<21];
14 lol g[1<<21],Mod=1e9+7,w[51],sum;
15 void add(int u,int v)
16 {
17   num++;
18   edge[num].next=head[u];
19   head[u]=num;
20   edge[num].to=v;
21 }
22 void add2(int u,int v)
23 {
24   num2++;
25   edge2[num2].next=head2[u];
26   head2[u]=num2;
27   edge2[num2].to=v;
28 }
29 void dfs(int x,int pa,int tp)
30 {int i;
31   dep[x]=dep[pa]+1;
32   if (in[x]!=2)
33     {
34       b[++cnt]=x;w[cnt]=dep[x]-dep[tp];
35       if (tp!=x)
36       add2(tp,x);
37       tp=x;
38     }
39   for (i=head[x];i;i=edge[i].next)
40     {
41       int v=edge[i].to;
42       if (v!=pa) dfs(v,x,tp);
43     }
44 }
45 int main()
46 {int i,u,v,j,k;
47   cin>>n;
48   for (i=1;i<=n-1;i++)
49     {
50       scanf("%d%d",&u,&v);
51       add(u,v);add(v,u);
52       in[u]++;in[v]++;
53     }
54   in[1]=-1;
55   dfs(1,0,1);
56   for (i=2;i<=n;i++)
57     if (in[i]==1)
58       l[++tot]=i,sz[i]=1;
59   for (i=cnt;i>=1;i--)
60     {
61       for (j=head2[b[i]];j;j=edge2[j].next)
62     {
63       int v=edge2[j].to;
64       sz[b[i]]+=sz[v];
65     }
66     }
67   f[0]=g[0]=1;
68   for (i=0;i<(1<<tot)-1;i++)
69     {
70       for (j=1;j<=cnt;j++)
71     s[b[j]]=0;
72       for (j=1;j<=tot;j++)
73     if (i&(1<<j-1))
74       s[l[j]]=1;
75       lol id=1;
76       for (j=cnt;j>=1;j--)
77     {
78       for (k=head2[b[j]];k;k=edge2[k].next)
79         {
80           int v=edge2[k].to;
81           s[b[j]]+=s[v];
82         }
83       if (s[b[j]]==sz[b[j]]) id+=w[j];
84     }
85       double as=f[i]*id;
86       for (j=1;j<=tot;j++)
87     if ((i&(1<<j-1))==0&&as>f[i|(1<<j-1)])
88       f[i|(1<<j-1)]=as,g[i|(1<<j-1)]=g[i]*id%Mod;
89     }
90   cout<<g[(1<<tot)-1];
91 }

原文地址:https://www.cnblogs.com/Y-E-T-I/p/8711048.html

时间: 2024-10-30 00:59:14

51nod 1673 树有几多愁的相关文章

[51nod1673]树有几多愁

lyk有一棵树,它想给这棵树重标号. 重标号后,这棵树的所有叶子节点的值为它到根的路径上的编号最小的点的编号. 这棵树的烦恼值为所有叶子节点的值的乘积. lyk想让这棵树的烦恼值最大,你只需输出最大烦恼值对1e9+7取模后的值就可以了. 注意一开始1号节点为根,重标号后这个节点仍然为根. update:数据保证叶子节点个数<=20. Input 第一行一个数n(1<=n<=100000). 接下来n-1行,每行两个数ai,bi(1<=ai,bi<=n),表示存在一条边连接这两

题解 [51nod1673] 树有几多愁

题面 解析 这题思路挺秒啊. 本麻瓜终于找了道好题了(还成功把ztlztl大仙拖下水了) 看到叶子节点数<=20就应该是状压啊. 然而DP要怎么写啊? 首先,考虑到编号肯定是从下往上一次增大的, 另外,对于没有分支的一条链,它的编号应该是连续的. 并且一种类似于贪心的想法就是一个点\(u\)被编号时它的子树一定被编号完了. 所以这也像是一个类似于拓扑序的东西. 先建一棵虚树(因为叶子节点只有20有很多没用的点),边权设为这条链上不在虚树上的点数. 设状态\(i\)表示状压后集合\(i\)中的点的

51nod 1462 树据结构 | 树链剖分 矩阵乘法

题目链接 51nod 1462 题目描述 给一颗以1为根的树. 每个点有两个权值:vi, ti,一开始全部是零. Q次操作: 读入o, u, d o = 1 对u到根上所有点的vi += d o = 2 对u到根上所有点的ti += vi * d 最后,输出每个点的ti值(n, Q <= 100000) 有50%的数据N,Q <= 10000 注:所有数64位整数不会爆. 题解 这道题好神奇啊--看讨论版里的 AntiLeaf 大神的矩阵乘法打标记才找到思路,然后又看到 ccz181078 的

51Nod 1405 树的距离之和 (dfs)

题目链接:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1405 中文题面不解释了,两次dfs,第一次自下向上,第二次自上向下. ans[i]表示i节点的答案,cnt[i]表示i节点为root的子树的节点个数,d[i]表示i节点为root的子树的答案. 1 //#pragma comment(linker, "/STACK:102400000, 102400000") 2 #include <algor

51Nod 1405 树的距离之和(dp)

1405 树的距离之和 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题  收藏  关注 给定一棵无根树,假设它有n个节点,节点编号从1到n, 求任意两点之间的距离(最短路径)之和. Input 第一行包含一个正整数n (n <= 100000),表示节点个数. 后面(n - 1)行,每行两个整数表示树的边. Output 每行一个整数,第i(i = 1,2,...n)行表示所有节点到第i个点的距离之和. Input示例 4 1 2 3 2 4 2 Output

[51nod]A树的双直径

题目链接: 51nod 树形\(DP\)+换根\(DP\). 最直观的想法是枚举一条边断开,在两颗子树中求最大直径相乘然后取最大值. 不过这题可能出现负数,那么答案可能是正数\(\times\)正数或者负数\(\times\)负数. 其实只需要考虑正数的情况(负数把边全部取反即可) 那么设枚举边\((x,y)\),其中\(x\)为\(y\)的父亲 那么我们需要求出以\(y\)为根子树中的最长链和除去\(x\)子树外的最长链 对于\(y\)子树中的最长链\(InMax[y]\),我们可以先考虑求出

51nod算法马拉松13

A 取余最长路 不难发现路径可以拆成三条线段,只要知道两个转折点的位置就能计算出答案. 设sum(i,l,r)表示第i行从l到r元素的和,则答案可以表示为sum(1,1,x)+sum(2,x,y)+sum(3,y,n)%p. 前缀和一下转化成(S3[n]-S3[y-1])+S2[y]+(S1[x]-S2[x-1])%p,从小到大枚举y,将所有(S1[x]-S2[x-1])扔到一个集合里,用个set就能轻松实现了. 时间复杂度为O(NlogN). #include<cstdio> #includ

20160218.CCPP体系详解(0028天)

程序片段(01):加法.c 内容概要:字符串计算表达式 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <stdlib.h> //01.去除空格: // 1.空格:空格+Tab键+类似符... // 2.双索引+双指针 // 变化快+变化慢:变化快覆盖变化慢 void removeBlankSpaceByIndex(char * pStr) {//双索引 int i = 0; int j = 0; //whil

我的一些诗词

我的一些诗词整理 校奖学金申请书··· 2 无题··· 3 弥望··· 3 相处论··· 3 记广州连日之大雨··· 4 路在何方··· 4 我之人生··· 4 中秋颂··· 5 Rothe法中有感··· 5 2008年春节祝福短信··· 5 无题··· 5 无题··· 6 无题··· 6 逐··· 6 汗---祝爷爷八十三岁生日快乐··· 6 人生路··· 7 天下第一··· 7 七一··· 7 我是一棵树 [英文] 8 2009年春节祝福短信··· 9 无题··· 9 登楼有怀··· 9 咏