[HAOI2015]树上染色

  考虑每条边对不同染色方案的贡献,用树包的方式DP。

  确定根,事先求出各个节点子树大小,对于u->v这条边,假设以v为根的子树中染了 k 个黑点,剩下的 size[v]-k 个为白点,这条边的贡献即为 e.val*(K-k)*k+e.val*(size[v]-k)*(N-K-(size[v]-k),N 为总点数,K 为要染黑的总点数。

  这样就可以DP了。

  设 f[u][j] 表示以 u 为根的子树中染 j 个黑点的最优值,之后用二维循环类似背包一样给 u 及其子节点分配染黑的节点数。

  在树上这个进行分配的环节都是用二维循环写的,写第一道树包是我就比较懵,写这道题时我更懵,不过现在再看的话挺套路的。

  还有复杂度,因为枚举点对所以总时间复杂度是 O ( n2 ),我并不会算。

  二十天前我交的程序T三个点,死活过不了啊,我说树包好难写,一下就写成 O ( n3 ) 的了,今天再写一遍竟然 A 掉了,于是我重评了一下之前的代码,,,,,也过了,还比今天的跑的快。所以说评测姬大人最近心情好了。

 

// q.c

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long LL;
const int M=2000+10;
struct Edge {
	int u,v,nex,val; Edge() {}
	Edge(int a,int b,int c,int d):u(a),v(b),nex(c),val(d) {}
}ed[M<<1];
int cnt,head[M];
void add_edge(int a,int b,int c) {
	ed[cnt]=Edge(a,b,head[a],c); head[a]=cnt++;
	ed[cnt]=Edge(b,a,head[b],c); head[b]=cnt++;
}
int N,K,size[M]; LL f[M][M];
void dfs1(int u) {
	size[u]=1;
	int i; Edge e;
	for(i=head[u];i!=-1;i=ed[i].nex) {
		e=ed[i];
		if(!size[e.v]) {
			dfs1(e.v);
			size[u]+=size[e.v];
		}
	}
}
void dfs(int u,int fa) {
	int i,j,k; LL tmp; Edge e;
	f[u][0]=f[u][1]=0;
	for(i=head[u];i!=-1;i=ed[i].nex) {
		e=ed[i];
		if(e.v!=fa) {
			dfs(e.v,u);
			for(j=size[u];j>=0;j--) {
				for(k=0;k<=j&&k<=size[e.v];k++) {
					tmp=(LL)(K-k)*k*e.val;
					tmp+=(LL)(size[e.v]-k)*(N-K-(size[e.v]-k))*e.val;
					tmp+=f[e.v][k];
					f[u][j]=max(f[u][j],f[u][j-k]+tmp);
				}
			}
		}
	}
}
int main() {
	freopen("haoi2015_t1.in","r",stdin);
	freopen("haoi2015_t1.out","w",stdout);
	memset(head,-1,sizeof(head));
	scanf("%d%d",&N,&K);
	int a,b,c;
	for(int i=1;i<N;i++) {
		scanf("%d%d%d",&a,&b,&c);
		add_edge(a,b,c);
	}
	for(int i=1;i<=N;i++) for(int j=0;j<=K;j++)
		f[i][j]=-(int)1e9;
	dfs1(1);
	dfs(1,0);
	printf("%lld\n",f[1][K]);
	return 0;
}

猜你喜欢

转载自www.cnblogs.com/qjs12/p/8856069.html