[HAOI2015]树上染色(树上dp)

[HAOI2015]树上染色

这种要算点对之间路径的长度和的题,难以统计每个点的贡献.这个时候一般考虑算每一条边贡献了哪些点对.
知道这个套路以后,那么这题就很好做了.
状态:设\(dp[u][i]\)表示u节点(子树里有i个黑点)的子树的边的贡献的和.
转移:转移就很好想了,知道v内的黑点个数j,知道v内的白点数目\(sz[v]-j\),知道总共的黑点数目\(m\),知道总共的白点数目\((n-m)\),知道边权w,那么转移方程显然就是:
\[ dp[u][i]=max{dp[v][j]+w*(m-j)*j+w*(sz_v-j)*[n-m-(sz_v-j)]} \]
答案就是\(dp[1][k]\)

#include<bits/stdc++.h>
#define maxn 2005
#define ll long long
using namespace std;
int n,m,sz[maxn];
ll dp[maxn][maxn],t[maxn];
int head[maxn],nxt[maxn<<1],to[maxn<<1],w[maxn<<1],cnt;
void add(int u,int v,int ww)
{
    nxt[++cnt]=head[u];head[u]=cnt;
    w[cnt]=ww;to[cnt]=v;
}
void dfs(int u,int ff)
{
    dp[u][0]=dp[u][1]=0;sz[u]=1;
    for(int ii=head[u];ii;ii=nxt[ii])
    {
        int v=to[ii];if(v==ff)continue;
        dfs(v,u);sz[u]+=sz[v];
        for(int i=0;i<=m;i++)t[i]=dp[u][i];
        for(int i=m;i>=0;i--)
        {
            for(int j=0;j<=min(i,sz[v]);j++)
            {
                if(t[i-j]==-1)continue;ll res=0,ww=w[ii];
                res=dp[v][j]+ww*(m-j)*j+ww*(sz[v]-j)*(n-m-sz[v]+j);
                //cout<<u<<" "<<v<<" "<<ww<<" "<<i<<" "<<j<<endl;
                dp[u][i]=max(dp[u][i],res+t[i-j]);
            }
        }
        //cout<<u<<"&"<<v<<" ";
        //for(int i=0;i<=m;i++)cout<<dp[u][i]<<" ";cout<<endl;
    }
}
int main()
{
    cin>>n>>m;
    for(int i=1,u,v,ww;i<n;i++)
        scanf("%d%d%d",&u,&v,&ww),add(u,v,ww),add(v,u,ww);
    memset(dp,-1,sizeof(dp));
    dfs(1,0);cout<<dp[1][m]<<endl;
    //for(int u=1;u<=n;u++,cout<<endl)
    //  for(int i=0;i<=m;i++)
    //      cout<<dp[u][i]<<" ";
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/terribleterrible/p/9861158.html
今日推荐