1. 程式人生 > >【BZOJ】4033: [HAOI2015]樹上染色 樹上背包

【BZOJ】4033: [HAOI2015]樹上染色 樹上背包

lap %d sca http 註意 表示 pos truct amp

【題目】#2124. 「HAOI2015」樹上染色

【題意】給定n個點的帶邊權樹,要求將k個點染成黑色,使得 [ 黑點的兩兩距離和+白點的兩兩距離和 ] 最大。n<=2000。

【算法】樹上背包

【題解】設f[i][j]表示子樹i中有j個黑點對答案的貢獻(包括點 i 到父親的邊 p ),由於邊p的貢獻只和 j 有關,所以最後再統計。

所以做樹上背包即可,註意這題特殊在f[x][0]≠0,所以初始f[x][k]+=f[y][0],然後不要把0作為物品。

最後統計邊p的貢獻:w[p] *(子樹內黑點*子樹外黑點+子樹內白點*子樹外白點)。

常數問題:要盡可能避免枚舉無用狀態,不然常數太大了,優化見代碼。

技術分享圖片
#include<cstdio>
#include
<cstring> #include<algorithm> #define ll long long using namespace std; const int maxn=2010,inf=0x3f3f3f3f; struct edge{int v,w,from;}e[maxn*2]; int first[maxn],tot,n,K,sz[maxn]; ll f[maxn][maxn]; void insert(int u,int v,int w){tot++;e[tot].v=v;e[tot].w=w;e[tot].from=first[u];first[u]=tot;} ll max(ll a,ll b){
return a<b?b:a;} void dfs(int x,int fa,int w){ for(int i=2;i<=K;i++)f[x][i]=-inf; sz[x]=1; for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){ dfs(e[i].v,x,e[i].w); sz[x]+=sz[e[i].v]; for(int k=min(sz[x],K);k>=0;k--){ f[x][k]+=f[e[i].v][0];// for
(int j=min(k,sz[e[i].v]);j>=1;j--)if(f[x][k-j]>-inf){// f[x][k]=max(f[x][k],f[x][k-j]+f[e[i].v][j]); }else break; } } for(int i=0;i<=K;i++)if(f[x][i]>-inf)f[x][i]+=1ll*w*(1ll*i*(K-i)+1ll*(sz[x]-i)*(n-K-sz[x]+i)); } int main(){ scanf("%d%d",&n,&K); for(int i=1;i<n;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); insert(u,v,w);insert(v,u,w); } dfs(1,0,0); printf("%lld",f[1][K]); return 0; }
View Code

【BZOJ】4033: [HAOI2015]樹上染色 樹上背包