51nod1673 樹有幾多愁(狀壓DP)
lyk有一棵樹,它想給這棵樹重標號。
重標號後,這棵樹的所有葉子節點的值為它到根的路徑上的編號最小的點的編號。
這棵樹的煩惱值為所有葉子節點的值的乘積。
lyk想讓這棵樹的煩惱值最大,你只需輸出最大煩惱值對1e9+7取模後的值就可以了。
注意一開始1號節點為根,重標號後這個節點仍然為根。:資料保證葉子節點個數<=20。
題解:狀壓DP。
首先想到對於每個葉子節點到根節點的鏈上的標號一定是單調遞增的(上面比下面優)。
同時,每個葉子節點到根節點未被標號的路徑會一次性被標號,也就是說只要確定了葉子節點的選擇順序,那麼整棵樹的標號也就確定,因為每次標號會將所有到根的路徑上未被標號的點給標號。
證明應該很明顯,因為是取到根節點的最小值,假設現在有兩條鏈,設給兩條鏈先後標號的兩個min值為
有了以上的結論,再看看葉子節點的個數:20,如果有高超的暴力技巧直接dfs剪枝就過了,不過我還是說一下正解:狀壓DP。
首先要想到狀壓DP應該不難,因為資料範圍很明顯,主要是怎麼DP,很容易想到用
怎麼狀態轉移?
加入一個點j到i狀態中,增加的乘積是j標號的點中的最小值。如果能夠求出j對於i能新增加多少個點,以及i已經染色的點的個數,那麼就很簡單了,接下來我說說怎麼處理,不妨記其為
首先,對於
問題轉化為求
#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();
}