[haoi2015]T1

 题意:给定你一颗树,要求你在这棵树中确定K个黑点和N-K个白点,使黑点间与白点间两两距离之和最大,输出最大值.n<=2000

 对于这道题,我想了好几个思路,包括点分治,贪心,动规,网络流等等,实在无法,敲了个贪心,但最后结果出来后题解是树形动规.

 动规尤其树形动规我也做过不少,这道题给了我很大启发.

 梳理一下我最初的否决的思路:设f[i][j]表示向i子树加入j个黑点所得到的最大收益,但我发现这样搞一是不好转移,二是后效性没有解除,三是复杂度惊人,就放弃了.

 得到正解之后,又仔细想了一下,有一个很明显的东西是,一个子树内部无论结构如何,只要确定它的黑点白点个数,对外面的影响就是确定的,这就是后效性的解释.

 转移可以视为背包,至于复杂度,复杂度ydc有专门的解释.

 这题就没什么了.

 

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<iomanip>
using namespace std;
#define LL long long
#define up(i,j,n) for(int i=j;i<=n;i++)
#define pii pair<int,int>
#define db double
#define eps 1e-4
#define FILE "dealing"
int read(){
	int x=0,f=1,ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+ch-'0',ch=getchar();}
	return x*f;
}
const LL maxn=2200,inf=10000000000000LL;
bool cmin(int& a,int b){return a>b?a=b,true:false;}
//bool cmax(int& a,int b){return a<b?a,b,true:false;}
bool cmin(LL& a,LL b){return a>b?a=b,true:false;}
int n,K;
struct node{
	int y,next,v;
}e[maxn<<1];
int linkk[maxn],len=0;
void insert(int x,int y,int v){e[++len].v=v;e[len].next=linkk[x];linkk[x]=len;e[len].y=y;}
LL f[maxn][maxn],siz[maxn],fa[maxn];
void dfs(int x){
	siz[x]=1;
	f[x][0]=f[x][1]=0;
	for(int i=linkk[x];i;i=e[i].next){
		if(e[i].y==fa[x])continue;
		fa[e[i].y]=x;
		dfs(e[i].y);
		siz[x]+=siz[e[i].y];
		for(int j=siz[x];j>=0;j--){
			for(int k=0;k<=siz[e[i].y]&&k<=j;k++){
				LL ans=k*(K-k)+(siz[e[i].y]-k)*(n-K-(siz[e[i].y]-k));
				ans*=e[i].v;
				ans+=f[e[i].y][k];
				f[x][j]=max((LL)f[x][j],f[x][j-k]+ans);
			}
		}
	}
}
int main(){
	freopen(FILE".in","r",stdin);
	freopen(FILE".out","w",stdout);
	n=read(),K=read();
	memset(f,-10,sizeof(f));
	up(i,1,n-1){
		int x=read(),y=read(),v=read();
		insert(x,y,v);insert(y,x,v);
	}
	dfs(1);
	cout<<f[1][K]<<endl;
	return 0;
}

  

posted @ 2017-02-27 21:47  CHADLZX  阅读(146)  评论(0编辑  收藏  举报