bzoj4033 [HAOI2015]树上染色

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4033

重要的思路:与其考虑每一个点对的贡献,不如考虑每条边的贡献(==被经过了几次)!

树形dp。

总共的黑点和白点的个数都是已知的,所以知道子树里有多少个黑点,就能算出子树的根到它的父亲的那条边被经过多少次。

  (因为子树中黑点数是包含根的,所以求子树向外的那条边比较方便)

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long
using namespace std;
const int N=2005;
int n,k,head[N],xnt,rt,siz[N];
ll dp[N][N],ed[N];
struct Edge{
    int next,to;
    ll w;
    Edge(int n=0,int t=0,ll c=0):next(n),to(t),w(c) {}
}edge[N<<1];
void add(int a,int b,ll c)
{
    edge[++xnt]=Edge(head[a],b,c);head[a]=xnt;
    edge[++xnt]=Edge(head[b],a,c);head[b]=xnt;
}
void dfs(int cr,int fa)
{
    siz[cr]=1;
    for(int i=head[cr],v;i;i=edge[i].next)
    {
        if((v=edge[i].to)==fa)continue;
        ed[v]=edge[i].w;
        dfs(v,cr);
        for(int j=min(k,siz[cr]+siz[v]);j>=0;j--)
            for(int l=max(0,j-siz[cr]);l<=j&&l<=siz[v];l++)
                dp[cr][j]=max(dp[cr][j],dp[cr][j-l]+dp[v][l]);
        siz[cr]+=siz[v];
    }
    if(!fa)return;
    for(int j=0;j<=k&&j<=siz[cr];j++)dp[cr][j]+=(j*(k-j)+(siz[cr]-j)*(n-k-(siz[cr]-j)))*ed[cr];
}
int main()
{
    scanf("%d%d",&n,&k);int x,y;ll z;
    for(int i=1;i<n;i++)
        scanf("%d%d%lld",&x,&y,&z),add(x,y,z);
    dfs(1,0);
    printf("%lld",dp[1][k]);
    return 0;
}

 

posted on 2018-06-10 17:17  Narh  阅读(177)  评论(0编辑  收藏  举报

导航