P3177 [HAOI2015]树上染色

P3177 [HAOI2015]树上染色

有一棵点数为 n 的树,树边有边权。给你一个在$ 0 \sim n $之内的正整数 k ,你要在这棵树中选择 k 个点,将其染成黑色,并将其他 的 n−k 个点染成白色。将所有点染色后,你会获得黑点两两之间的距离加上白点两两之间的距离的和的受益。问受益最大值是多少。

\(0\le n,k\le2000\)

\[手画几个图显然可以发现,枚举每个边,显然这条边对答案的贡献为边两侧同色点个数的乘积\\ 每条边被经过的次数tot=m*(k-m)+(size[v]-m)*(n-k-size[v]+m)\\ 黑色点贡献m*(k-m)\ \ m为当前子节点子树上选择的黑点数\\ f[u][j]为当前在以u为根节点的子树,选了j个黑点的最大贡献\\ f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]+tot*e[i].w) \]

代码明天放 咕咕咕
写了50pts 咕咕咕
开了long long95 pts咕咕咕
开了02 过了 eee代码:

#include<cstdio>
#include<cstring>
#define maxn 2001
#define int long long
using namespace std;
int f[maxn][maxn];
int min(int a,int b){return a<b?a:b;}
int max(int a,int b){return a>b?a:b;}
inline int read(){
	int s = 0,w = 1;
	char ch = getchar();
	for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') w = -1;
	for(; ch <= '9' && ch >= '0'; ch = getchar()) s = s * 10 + ch - '0';
	return s * w;
}
struct edge{
	int to,next,w,nxt;
}e[maxn << 1];
int n,m,fr,to,val,k,tot=0;
int head[maxn],siz[maxn];
inline void add(int u,int v,int w){
	e[tot].w=w,e[tot].to=v,e[tot].next=head[u];head[u]=tot++;
}
void dfs(int u,int fa){
	siz[u] = 1;memset(f[u],-1,sizeof(f[u]));f[u][0]=f[u][1]=0;
	for(int i=head[u];~i;i=e[i].next){
		int v=e[i].to;if(v == fa) continue;
		dfs(v,u);siz[u] += siz[v];
		for(int j=min(m,siz[u]);j>=0;j--){
			if(f[u][j]!=-1)
			f[u][j]+=f[v][0]+(int)siz[v]*(n-m-siz[v])*e[i].w;
			for(int k=min(j,siz[v]);k;--k){
				if(f[u][j-k]==-1)continue;
				int val=(int)(k*(m-k)+(siz[v]-k)*(n-m-siz[v]+k))*e[i].w;   //当前情况下连接子节点的边的贡献
				f[u][j]=max(f[u][j],f[u][j-k]+f[v][k]+val);
		}
	}
}
}
signed main(){
	memset(head,-1,sizeof(head));
	n=read();m=read();
	if(n-m<m)m=n-m;
	for(int i=1;i<n;++i){
		int u=read(),v=read(),w=read();
		add(u,v,w);add(v,u,w);
	}memset(f,-1,sizeof(f));
	dfs(1,-1);
	printf("%lld",f[1][m]);
}
posted @ 2020-07-13 22:12  INFP  阅读(125)  评论(0编辑  收藏  举报