1. 程式人生 > >【題解】 bzoj4033: [HAOI2015]樹上染色* (動態規劃)

【題解】 bzoj4033: [HAOI2015]樹上染色* (動態規劃)

規劃 math online 4.2 pro php 白色 AD truct

bzoj4033,懶得復制,戳我戳我

Solution:

  • 定義狀態\(dp[i][j]\)表示\(i\)號節點為根節點的子樹裏面有\(j\)個黑色節點時最大的貢獻值
  • 然後我們要知道的就是子節點到根節點這條邊會計算次數就是:子樹中白色節點數\(*\)子樹外白色節點數\(+\)子樹中黑色節點數\(*\)子樹外黑色節點數
    \[dp[u][j+k]=max(dp[u][j+k],\]
    \[dp[u][j]+dp[v][k]+(1ll)*k*(m-k)*dis[v]+(1ll)*(siz[v]-k)*(n-m-siz[v]+k)*dis[v])\]
  • 丟個鏈接

Attention:

  • 樹上背包dp註意操作:
  • 這樣可以保證時間復雜度是\(O(n^2)\),每次會保證是從已經獲得的dp值推向未知的,就不會有多余的操作,所以我們每次枚舉要添加的節點數目,加到已經求出前面幾棵子樹節點數目中

    for(int j=min(m,siz[u]);j>=0;j--){
      int box=min(m,siz[v]);
      for(int k=box;k>=0;k--){
         dp[u][j+k]=max(dp[u][j+k],dp[u][j]+dp[v][k]+1ll*k*(m-k)*dis[v]+1ll*(siz[v]-k)*(n-m-siz[v]+k)*dis[v]);
      }
    }siz[u]+=siz[v];

Code:

//It is coded by Ning_Mew on 4.24
#include<bits/stdc++.h>
#define LL long long
using namespace std;

const int maxn=2000+7;

int n,m,fa[maxn];
LL dp[maxn][maxn];
int siz[maxn],dis[maxn];
int head[maxn],cnt=0;
struct Edge{int nxt,to,dis;}edge[maxn*2];

void add(int from,int to,int dis){
  edge[++cnt].nxt=head[from];
  edge[cnt].to=to;
  edge[cnt].dis=dis;
  head[from]=cnt;
}

void
dfs(int u){ siz[u]=1;//dp[u][0]=dp[u][1]=0; for(int i=head[u];i!=0;i=edge[i].nxt){ int v=edge[i].to; if(v==fa[u])continue; dis[v]=edge[i].dis; fa[v]=u; dfs(v); //siz[u]+=siz[v]; for(int j=min(m,siz[u]);j>=0;j--){ int box=min(m,siz[v]); for(int k=box;k>=0;k--){ dp[u][j+k]=max(dp[u][j+k],dp[u][j]+dp[v][k]+1ll*k*(m-k)*dis[v] +1ll*(siz[v]-k)*(n-m-siz[v]+k)*dis[v]); } }siz[u]+=siz[v]; }return; } int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n-1;i++){ int u,v,diss;scanf("%d%d%d",&u,&v,&diss); add(u,v,diss);add(v,u,diss); } dfs(1); printf("%lld\n",dp[1][m]); return 0; }

【題解】 bzoj4033: [HAOI2015]樹上染色* (動態規劃)