51nod 1673 树有几多愁

Description

lyk有一棵树,它想给这棵树重标号。
重标号后,这棵树的所有叶子节点的值为它到根的路径上的编号最小的点的编号。
这棵树的烦恼值为所有叶子节点的值的乘积。
lyk想让这棵树的烦恼值最大,你只需输出最大烦恼值对1e9+7取模后的值就可以了。
注意一开始1号节点为根,重标号后这个节点仍然为根。
数据保证叶子节点个数<=20,N<=100000

Solution

容易想到深度越大的节点编号要尽量小,且从叶子到根节点经过的节点的编号是递增的
因为我们要让叶子节点编号尽量的大,所以能往上多编号就多编号
注意到一个节点子树内的点的编号如果都确定了,为了符合上述贪心策略,就可以直接确定这个点的编号了,为(size+1)
然后发现儿子数为 (1)的节点是没有用的,因为可以直接一路确定上去,只用知道这样的点的个数即可
把这些点去掉之后,由于叶子节点数只有(20),所以去掉之后,剩下的点不超过 (39) 个了,于是可以暴力做了
把叶子节点状压 , 设 (f[i]) 表示状态为 (i) 的节点烦恼值的最大值
让能确定编号的点都确定好,得到下一个节点的编号,枚举把这个编号给哪个叶子即可

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10,mod=1e9+7;
int head[N],nxt[N<<2],to[N<<2],num=0,n,in[N],sz[N],v[N],leaf[N];
int Head[N],b[N],cnt=0,sum=0,w[N],g[1<<20];double f[1<<20];
inline void link(int x,int y){nxt[++num]=head[x];to[num]=y;head[x]=num;}
inline void Link(int x,int y){nxt[++num]=Head[x];to[num]=y;Head[x]=num;}
inline void dfs(int x,int last,int fa){
	sum++;
	if(in[x]!=2){
		b[++cnt]=x;w[cnt]=sum;sum=0;
		if(fa!=x)Link(fa,x),fa=x;
	}
	for(int i=head[x];i;i=nxt[i])if(to[i]!=last)dfs(to[i],x,fa);
}
int main(){
  freopen("pp.in","r",stdin);
  freopen("pp.out","w",stdout);
  int x,y,m=0,tot;
  scanf("%d",&n);
  for(int i=1;i<n;i++){
	  scanf("%d%d",&x,&y);
	  link(x,y);link(y,x);
	  in[x]++;in[y]++;
  }
  in[1]=-1;dfs(1,1,1);
  for(int i=2;i<=n;i++)if(in[i]==1)leaf[++m]=i,sz[i]=1;
  for(int i=cnt;i>=1;i--)
	  for(int j=Head[b[i]];j;j=nxt[j])
		  sz[b[i]]+=sz[to[j]];
  tot=(1<<m)-1;
  f[0]=g[0]=1;
  for(int i=0;i<tot;i++){
	  for(int j=1;j<=cnt;j++)v[b[j]]=0;
	  for(int j=1;j<=m;j++)
		  if(i&(1<<(j-1)))v[leaf[j]]=1;
	  int id=1;
	  for(int j=cnt;j>=1;j--){
		  for(int k=Head[b[j]];k;k=nxt[k])
			  v[b[j]]+=v[to[k]];
		  if(v[b[j]]==sz[b[j]])id+=w[j];
	  }
	  double t=f[i]*id;
	  for(int j=1;j<=m;j++)
		  if(!(i&(1<<(j-1))) && t>f[1<<(j-1)|i])
			  f[1<<(j-1)|i]=t,g[1<<(j-1)|i]=1ll*id*g[i]%mod;
  }
  cout<<g[tot]<<endl;
  return 0;
}

原文地址:https://www.cnblogs.com/Yuzao/p/8460118.html