1. 程式人生 > >51nod1673 樹有幾多愁(狀壓DP)

51nod1673 樹有幾多愁(狀壓DP)

傳送門

lyk有一棵樹,它想給這棵樹重標號。
重標號後,這棵樹的所有葉子節點的值為它到根的路徑上的編號最小的點的編號。
這棵樹的煩惱值為所有葉子節點的值的乘積。
lyk想讓這棵樹的煩惱值最大,你只需輸出最大煩惱值對1e9+7取模後的值就可以了。
注意一開始1號節點為根,重標號後這個節點仍然為根。:資料保證葉子節點個數<=20。

題解:狀壓DP。

首先想到對於每個葉子節點到根節點的鏈上的標號一定是單調遞增的(上面比下面優)。
同時,每個葉子節點到根節點未被標號的路徑會一次性被標號,也就是說只要確定了葉子節點的選擇順序,那麼整棵樹的標號也就確定,因為每次標號會將所有到根的路徑上未被標號的點給標號。

證明應該很明顯,因為是取到根節點的最小值,假設現在有兩條鏈,設給兩條鏈先後標號的兩個min值為min1,min2,且min1為較小值,那麼如果同時標號必然會是min1,min3,且min3min2因為最小的值肯定不變,而同時標號會導致另一條鏈上的最小值變小。

有了以上的結論,再看看葉子節點的個數: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對狀態(i(1<<j))的距離加上cnt[(i(1<<j))]。這個明顯可以DP。

問題轉化為求dis[j][i]。這個值其實就是ji中已經染色的點的最小距離。可以O(n)求,當然,這種做法很低效,因為j到狀態i

中的很多染色的點在狀態i的子狀態中已經列舉過了。只需列舉任意一個在狀態i中的點o,取min{dis[j][lca(i,o)],dis[j][i(1<<o)]}就好了,這個問題也得到了線性做法。

#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();
}