题目分析(以下的M都是题目当中输入的K):
树形DP(好吧这是废话)。我们按照树形DP的套路(啥?你不知道树形DP的套路?)很容易得出这道题的状态设定:
DP[I][J]表示以I为根的子树当中涂了J个黑点的最大收益,J<=M
那么问题来了,我们应该如何进行状态转移,关键点在于应该如何处理状态转移时发生的收益变化?
让我们来考虑这样一个问题:
树上一条边Edge(X,Y),不妨设X为Y的父亲节点,我们假设不在以X为根的子树外(包括X点)一共有P个黑点,那么在以X为根的子树内也就是Y内(不包括X点,包括Y点)一共有M-P个黑点。关键点来了,根据乘法原理所有的黑点连边那么一共有P*(M-P)条边,这P*(K-P)条边都经过同一条边X->Y,白点同此。这样我们就将需要计算的距离转化为了某一条边的计算,这样就方便了我们的转移。
于是就可以得到下面的状态转移方程了(其实是一个树上背包的过程):
Size[X]表示以X为根节点的子树的大小,
DP[X][J]=max{DP[X][J],DP[X][J-K]+DP[Y][K]+K*(J-K)*(X->Y)+(Size[Y]-K)*(N-M-Size[Y]+K)*(X->Y)}
由于在这棵树当中哪一个点是根节点都是相对的,为了方便讨论与写代码,都以1号点为根节点,所以最后的答案为DP[1][M]。
另,这道题似乎卡时,大家就不要在不必要的地方开long long 了
于是现在代码就很好写啦,关键代码:
void Tree_DP(int X,int Fx){
int I,J,K;Size[X]=1;
for(I=Head[X];I;I=Next[I]){
int Y=To[I];
if(Y!=Fx){
Tree_DP(Y,X);
Size[X]+=Size[Y];
}
}
for(I=0;I<=Size[X];I++){
DP[X][I]=-1ll;
}DP[X][0]=DP[X][1]=0ll;
for(I=Head[X];I;I=Next[I]){
int Y=To[I];LL Z=Edge[I];
if(Y!=Fx){
int NumX=min(M,Size[X]);
for(J=NumX;J>=0;J--){
int NumY=min(J,Size[Y]);
for(K=0;K<=min(NumY,J);K++){
if(DP[X][J-K]!=-1ll){
DP[X][J]=max(DP[X][J],DP[X][J-K]+DP[Y][K]+(LL)K*(M-K)*Z+(LL)(Size[Y]-K)*(N-M-Size[Y]+K)*Z);
}
}
}
}
}
}