1. 程式人生 > >[HAOI2015]樹上染色(樹形背包)

[HAOI2015]樹上染色(樹形背包)

for out print ios 黑點 bzoj 經典的 getchar struct

有一棵點數為 N 的樹,樹邊有邊權。給你一個在 0~ N 之內的正整數 K ,你要在這棵樹中選擇 K個點,將其染成黑色,並將其他 的N-K個點染成白色 。 將所有點染色後,你會獲得黑點兩兩之間的距離加上白點兩兩之間的距離的和的受益。問受益最大值是多少。

Solution

比較經典的樹形背包問題。

如果只對點進行分析,情況會變得十分麻煩,不放考慮每條變的貢獻,每條邊會產生兩邊黑點數的乘積加上兩邊白點數的乘積。

這樣的話我們直接跑背包就可以了,標準的樹形背包是n^3的,但是這道題每顆字數背包體積有上限,總復雜度可以做到n^2.

Code

#include<iostream>
#include
<cstdio> #include<cstring> #define N 2009 using namespace std; long long dp[N][N]; int size[N],m,n,a,b,c,tot,head[N]; struct dsd { int n,to,l; }an[N<<1]; inline void add(int u,int v,int l) { an[++tot].n=head[u]; an[tot].to=v; head[u]=tot; an[tot].l=l; } void dfs(int
u,int fa) { size[u]=1; for(int i=head[u];i;i=an[i].n) if(an[i].to!=fa) { int v=an[i].to; dfs(v,u); size[u]+=size[v]; for(int j=min(m,size[u]);j>=0;--j)// for(int k=0;k<=min(j,size[v]);++k) if(dp[v][k]!=-0x3f3f3f3f) { long long num=(long long
)(k*(m-k)+(n-size[v]-(m-k))*(size[v]-k))*an[i].l; dp[u][j]=max(dp[u][j],dp[u][j-k]+dp[v][k]+num); } } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<n;++i) { scanf("%d%d%d",&a,&b,&c); add(a,b,c);add(b,a,c); } memset(dp,-0x3f,sizeof(dp)); for(int i=1;i<=n;++i) dp[i][0]=dp[i][1]=0;// dfs(1,0); cout<<dp[1][m]; return 0; }

這種寫法太慢了,並沒有做到嚴格n^2,bzoj會TLE,下面這種寫法是穩過的。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#define N 2009
using namespace std;
typedef long long ll;
ll dp[N][N],size[N],m,n,a,b,c,tot,head[N],g[N];
struct dsd
{
    ll n,to,l;
}an[N<<1];
inline void add(ll u,ll v,ll l)
{
    an[++tot].n=head[u];
    an[tot].to=v;
    head[u]=tot;
    an[tot].l=l;
}
ll mi(ll x,ll y){return x<y?x:y;}
ll ma(ll x,ll y){return x<y?y:x;}
void dfs(ll u,ll fa){
  size[u]=1;
  for(ll i=head[u];i;i=an[i].n)
  if(an[i].to!=fa){
      ll v=an[i].to;
      dfs(v,u);
    ll x=mi(m,size[u]),y=mi(m,size[v]);
    for(int j=0;j<=m;++j)g[j]=0;
    for(ll j=x;j>=0;--j)
      for(int k=0;k<=y;++k)if(j+k<=m){
          ll gyx=((ll)k*(m-k)+(n-size[v]-(m-k))*(size[v]-k))*an[i].l;
          g[j+k]=ma(g[j+k],dp[u][j]+dp[v][k]+gyx);
      }
    for(int j=0;j<=m;++j)dp[u][j]=g[j];
    size[u]+=size[v];
  } 
}
inline int rd(){
    int x=0;char c=getchar();
    while(!isdigit(c))c=getchar();
    while(isdigit(c)){
        x=(x<<1)+(x<<3)+(c^48);
        c=getchar();
    }
    return x;
} 
int main()
{
    n=rd();m=rd();
    for(int i=1;i<n;++i){
      a=rd();b=rd();c=rd();
      add(a,b,c);add(b,a,c);
    }
    memset(dp,-0x3f,sizeof(dp));
    for(int i=1;i<=n;++i)
      dp[i][0]=dp[i][1]=0;
    dfs(1,0);
    printf("%lld",dp[1][m]);
    return 0;
} 

[HAOI2015]樹上染色(樹形背包)