[HAOI2015]树上染色

Luogu P3177

树形DP题

一开始真难想。

关键思路在于统计每条边的贡献。

设 f[u][j]表示以u为根的子树 有j个黑点,对答案的贡献。

递推式子比较好写f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]+val) 

          val=ed[i].w*(k*(m-k)+(siz[v]-k)*(n-m-siz[v]+k))  ————记得开long long

          val表示该边左右的黑点相连的方案数+白点相连的方案数。

    交了坐等AC………………………………80分!!!!!

      原因竟是…………………f数组要赋初值为-1!  

       百思不得其解,去问郭老师。

      gls认真讲题(真帅):赋初值为-1表示该状态不合法。如果不赋初值为-1,会导致某一个状态是由一个不合法的状态转移来的,(例如一个size为4的子树有6个黑节点)。dfs过程中,f[x][0]=f[x][1]=0表示该状态为合法的。状态转移时判断一下 f[x][j-k]!=-1 就行了。

     嗯,真棒,A了。

    以下是代码:

#include<bits/stdc++.h>
#define ll long long
#define RE register
using namespace std;
const int maxn=2100;
inline int R(){
    RE char b=getchar();int x=0,t=1;
    while(b<'0'||b>'9'){if(b=='-') t=-1;b=getchar();}
    while(b>='0'&&b<='9') {x=(x<<3)+(x<<1)+b-'0';b=getchar();}
    return x*t;
}
struct Edge{
    int nxt,to,w;
}ed[maxn<<1];
int head[maxn],ecnt;
void addedge(int f,int to,int w){
    ed[++ecnt].to=to;
    ed[ecnt].w=w;
    ed[ecnt].nxt=head[f];
    head[f]=ecnt;
}
int n,m;
int siz[maxn];
ll f[maxn][maxn];
void dfs(int x,int fa){
    siz[x]=1;
    f[x][0]=f[x][1]=0;
    for(int i=head[x];i;i=ed[i].nxt){
        int v=ed[i].to;
        if(v==fa) continue;
        dfs(v,x);
        siz[x]+=siz[v];
        for(int j=min(m,siz[x]);j>=0;j--)
            for(int k=0;k<=min(j,siz[v]);k++)
                if(f[x][j-k]!=-1) f[x][j]=max(f[x][j],f[x][j-k]+f[v][k]+1ll*ed[i].w*(1ll*k*(m-k)+1ll*(siz[v]-k)*(n-m-siz[v]+k)));
    }
}
int main(){
    n=R(),m=R();
    memset(f,-1,sizeof f);
    for(int i=1,a,b,c;i<=n-1;i++){
        a=R(),b=R(),c=R();
        addedge(a,b,c);
        addedge(b,a,c);
    }
    dfs(1,0);
    printf("%lld",f[1][m]);
    return 0;
}

 

转载于:https://www.cnblogs.com/sdfzjdx/p/11269047.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值