lyk有一棵树,它想给这棵树重标号。
重标号后,这棵树的所有叶子节点的值为它到根的路径上的编号最小的点的编号。
这棵树的烦恼值为所有叶子节点的值的乘积。
lyk想让这棵树的烦恼值最大,你只需输出最大烦恼值对1e9+7取模后的值就可以了。
注意一开始1号节点为根,重标号后这个节点仍然为根。:数据保证叶子节点个数<=20。
题解:状压DP。
首先想到对于每个叶子节点到根节点的链上的标号一定是单调递增的(上面比下面优)。
同时,每个叶子节点到根节点未被标号的路径会一次性被标号,也就是说只要确定了叶子节点的选择顺序,那么整棵树的标号也就确定,因为每次标号会将所有到根的路径上未被标号的点给标号。
证明应该很明显,因为是取到根节点的最小值,假设现在有两条链,设给两条链先后标号的两个min值为 min1,min2 ,且 min1 为较小值,那么如果同时标号必然会是 min1,min3 ,且 min3≤min2 因为最小的值肯定不变,而同时标号会导致另一条链上的最小值变小。
有了以上的结论,再看看叶子节点的个数:20,如果有高超的暴力技巧直接dfs剪枝就过了,不过我还是说一下正解:状压DP。
首先要想到状压DP应该不难,因为数据范围很明显,主要是怎么DP,很容易想到用 dp[i] 表示当前已染色叶子节点的状态以及最优乘积。(注意,这里因为有mod的存在,为了避免高精度,最好取对数存入另一个数组来比较大小)
怎么状态转移?
加入一个点j到i状态中,增加的乘积是j标号的点中的最小值。如果能够求出j对于i能新增加多少个点,以及i已经染色的点的个数,那么就很简单了,接下来我说说怎么处理,不妨记其为
dis[j][i]和cnt[i]
首先,对于
cnt[i]
。若其只有一个数,那么就等于这个数到根节点的距离,否则,枚举任意在状态i中的已经选择的点j,
cnt[i]
就等于
j
对状态
问题转化为求
dis[j][i]
。这个值其实就是
j
到
#include<bits/stdc++.h>
using namespace std;
int n;
namespace Task1{
int a[12],fa[12],ans,deg[12],vis[12];
vector<int>edge[12];
inline void dfs(int now,int f){
fa[now]=f;
for(int e=edge[now].size()-1;e>=0;e--){
int v=edge[now][e];
if(v==f)continue;
deg[now]++;dfs(v,now);
}
}
inline int getfa(int now){
int t=n;
while(now){
t=min(t,a[now]);
now=fa[now];
}
return t;
}
inline int calc(){
int res=1;
for(int i=1;i<=n;i++){
if(!deg[i])res*=getfa(i);
}
return res;
}
inline void dfs2(int now){
if(now==n+1){
ans=max(ans,calc());
return;
}
for(int i=1;i<=n;i++){
if(vis[i])continue;
a[now]=i;vis[i]=1;
dfs2(now+1);
vis[i]=0;
}
}
inline void solve(){
for(int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
edge[x].push_back(y);edge[y].push_back(x);
}
dfs(1,0);
dfs2(1);
cout<<ans<<endl;
}
}
namespace Task2{
typedef long long ll;
const int LIM=(1<<20)+20,Maxn=1e5+50;
const ll Mod=1e9+7;
int ind,tot,dep[Maxn],top[Maxn],son[Maxn],fa[Maxn],lim,id[25];
int dis[20][LIM],cnt[LIM],ans[LIM];
int nxt[Maxn*2],to[Maxn*2],last[Maxn],ecnt;
double dp[LIM];
inline int read(){
char ch=getchar();int i=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}
return i*f;
}
int sze[Maxn];
inline int dfs(int now,int f){
dep[now]=dep[f]+1;fa[now]=f;
int sze=1,mxsze=0,bz=1;
for(int e=last[now];e;e=nxt[e]){
int v=to[e];
if(v==f)continue;
bz=0;
int t=dfs(v,now);sze+=t;
if(!son[now]||mxsze<t)son[now]=v,mxsze=t;
}
if(now!=1&&bz){
id[tot++]=now;
}
return sze;
}
inline void dfstop(int now,int f){
if(son[now]){
top[son[now]]=top[now];
dfstop(son[now],now);
}
for(int e=last[now];e;e=nxt[e]){
int v=to[e];
if(v==f||v==son[now])continue;
top[v]=v;
dfstop(v,now);
}
}
inline int getlca(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
x=fa[top[x]];
}
return (dep[x]>dep[y])?y:x;
}
inline long long calcdis(int x,int y){
int lca=getlca(x,y);
return dep[x]+dep[y]-2*dep[lca];
}
inline void pre(){
dfs(1,0);
top[1]=ind=1;
dfstop(1,0);
tot--;
lim=(1<<(tot+1))-1;
for(int i=0;i<=tot;i++)dis[i][0]=dep[id[i]];
for(int i=1;i<=lim;i++){
for(int j=0;(1<<j)<=i;j++){
if(i&(1<<j)){
if(i^(1<<j)){
int t=i^(1<<j);
for(int o=0;(1<<o)<=t;++o){
if(t&(1<<o)){
dis[j][t]=min(dis[j][t^(1<<o)],dep[id[j]]-dep[getlca(id[j],id[o])]);
cnt[i]=cnt[t]+dis[j][t];
break;
}
}
}else{
cnt[i]=dep[id[j]];
dis[j][0]=dep[id[j]];
break;
}
}
}
}
}
inline void add(int x,int y){
nxt[++ecnt]=last[x];last[x]=ecnt;to[ecnt]=y;
}
inline void solve(){
double mx;
for(int i=1;i<n;i++){
int x=read(),y=read();
add(x,y);
add(y,x);
}
pre();
dp[0]=0;ans[0]=1;
for(int i=1;i<=lim;i++){
mx=-1;
for(int j=0;(1<<j)<=i;++j){
if((1<<j)&i){
double t=dp[i^(1<<j)]+log2(n-cnt[i]+1);
if(t>mx)mx=t,ans[i]=1ll*ans[i^(1<<j)]*(n-cnt[i]+1)%Mod;
}
}
dp[i]=mx;
}
cout<<ans[lim]<<endl;
}
}
int main(){
scanf("%d",&n);
if(n<=9)Task1::solve();
else Task2::solve();
}