題解 HDU6566 【The Hanged Man】
阿新 • • 發佈:2020-09-19
題意為給定 \(n\) 個點的樹,每個節點為一個物品,有體積和價值,選物品必須滿足不相鄰,即選出一個獨立集,求對於 \(\forall i \in [1,m]\),容量為 \(i\) 時的揹包最大價值的方案數。
\(1 \leqslant n \leqslant 50,1 \leqslant m \leqslant 5000\)
暴力就是直接樹形揹包,\(f_{i,j,0/1}\) 為在 \(i\) 的子樹內,容量為 \(j\),是否選 \(i\) 的方案數,但複雜度為 \(O(nm^2)\),無法接受。
考慮用 \(dfs\) 序轉移來優化,但是發現從 \(dfs\) 序中 \(i\) 轉移到 \(i+1\)
因此還需知道像圖中 \(i+1\) 的父親那樣的轉折點的選擇情況,直接狀壓一個點到根節點路徑上的所有轉折點不現實,會被鏈卡成狀態數為 \(O(2^n)\)。
考慮優化狀態數,先進行重鏈剖分,剖分重鏈時優先遍歷輕兒子,因為最後才遍歷重兒子,所以一個點到根節點的所有轉折點都是重鏈的鏈底,那麼再進行狀壓,狀態數就為 \(O(2^{\log n})=O(n)\) 了。
設 \(f_{i,S,j}\) 為考慮到 \(dfs\) 序中第 \(i\) 個點,到根的轉折點的狀態為 \(S\)
另外一個做法是進行點分治,將每次的分治中心作為轉折點來狀壓,這樣狀態數也是 \(O(n)\) 的。
#include<bits/stdc++.h> #define maxn 210 #define maxm 5010 using namespace std; typedef long long ll; template<typename T> inline void read(T &x) { x=0;char c=getchar();bool flag=false; while(!isdigit(c)){if(c=='-')flag=true;c=getchar();} while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();} if(flag)x=-x; } int T,n,m,cnt,now; int w[maxn],v[maxn],id[maxn]; int siz[maxn],fa[maxn],son[maxn],top[maxn],rev[maxn]; vector<int> t[maxn]; struct edge { int to,nxt; }e[maxn]; int head[maxn],edge_cnt; void add(int from,int to) { e[++edge_cnt]={to,head[from]},head[from]=edge_cnt; } struct node { int v; ll cnt; }f[2][maxn][maxm],ans[maxm]; node operator +(const node &x,const int &val) { return {x.v+val,x.cnt}; } void mx(node &x,node y) { if(x.v<y.v) x=y; else if(x.v==y.v) x.cnt+=y.cnt; } void dfs_son(int x,int fath) { fa[x]=fath,siz[x]=1; for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(y==fath) continue; dfs_son(y,x),siz[x]+=siz[y]; if(siz[y]>siz[son[x]]) son[x]=y; } } void dfs_chain(int x,int tp) { top[x]=tp,rev[++cnt]=x; for(int i=head[x];i;i=e[i].nxt) { int y=e[i].to; if(y==fa[x]||y==son[x]) continue; dfs_chain(y,y); } if(son[x]) dfs_chain(son[x],tp); } void clear() { edge_cnt=cnt=0,now=1; memset(f,0,sizeof(f)); memset(ans,0,sizeof(ans)); memset(son,0,sizeof(son)); memset(head,0,sizeof(head)); for(int i=1;i<=n;++i) t[i].clear(); } void solve(int num) { read(n),read(m),clear(); for(int i=1;i<=n;++i) read(w[i]),read(v[i]); for(int i=1;i<n;++i) { int x,y; read(x),read(y); add(x,y),add(y,x); } dfs_son(1,0),dfs_chain(1,1); for(int i=1;i<=n;++i) { int p=i; t[i].push_back(i); while(fa[top[p]]) p=fa[top[p]],t[i].push_back(p); } f[0][0][0]={0,1}; for(int i=1;i<=n;++i) { for(int s=0;s<(1<<t[rev[i]].size());++s) for(int j=0;j<=m;++j) f[now][s][j]={0,0}; int fath=20; for(int j=0;j<t[rev[i-1]].size();++j) { int p=t[rev[i-1]][j]; if(fa[rev[i]]==p) fath=j; id[j]=-1; for(int k=0;k<t[rev[i]].size();++k) if(p==t[rev[i]][k]) id[j]=k; } for(int s=0;s<(1<<t[rev[i-1]].size());++s) { int S=0; for(int j=0;j<t[rev[i-1]].size();++j) if((s&(1<<j))&&id[j]!=-1) S|=1<<id[j]; for(int j=0;j<=m;++j) { if(!f[now^1][s][j].cnt) continue; mx(f[now][S][j],f[now^1][s][j]); if((s&(1<<fath))||j+w[rev[i]]>m) continue; mx(f[now][S+1][j+w[rev[i]]],f[now^1][s][j]+v[rev[i]]); } } now^=1; } for(int s=0;s<(1<<t[rev[n]].size());++s) for(int i=1;i<=m;++i) mx(ans[i],f[now^1][s][i]); printf("Case %d:\n",num); for(int i=1;i<=m;++i) printf("%lld%c",ans[i].cnt," \n"[i==m]); } int main() { read(T); for(int i=1;i<=T;++i) solve(i); return 0; }