• BZOJ4033 T1


    Description

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

    Input

    第一行包含两个整数(N,K)
    接下来(N-1)行每行三个正整数(fr, to, dis),表示该树中存在一条长度为(dis)的边((fr, to))。输入保证所有点之间是联通的。

    Output

    输出一个正整数,表示收益的最大值。

    Sample Input

    3 1
    1 2 1
    1 3 2

    Sample Output

    3

    HINT

    对于(100\%)的数据,(0 le K le N le 2000)

    一道很好的树形dp题。(f_{i,j})表示以(i)为根的子树中染(j)个黑点的最大收益,那么问题就来了,应该怎么转移?
    假设我们此时已经dp到(now)这个跟,试着更新(f_{now,j}),现在用其儿子(c)来更新。假设连接(now)(c)的边权值(dis),枚举(f_{c,k}),之后就计算此边贡献即可。黑点这条边经过了((K-k) imes k)次(所有黑点,并不只是(now)子树中),故贡献((K-k) imes k imes dis)。白点也这样算即可。故转移方程为

    [f_{now,j} = max(f_{now,j},f_{now,j-k}+f_{c,k}+(K-k) imes k imes dis+(N-K-(size-k)) imes (size-k) imes dis) ]

    其中(size)(c)的子树大小。
    代码如下:

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    using namespace std;
    
    #define maxn (2010)
    typedef long long ll;
    int cnt,N,K,len[maxn*2],next[maxn*2],toit[maxn*2],side[maxn]; ll f[maxn][maxn];
    
    inline void add(int a,int b,int c) { next[++cnt] = side[a]; side[a] = cnt; toit[cnt] = b; len[cnt] = c; }
    inline void ins(int a,int b,int c) { add(a,b,c); add(b,a,c); }
    
    inline void upd(ll &a,ll b) { a = max(a,b); }
    
    inline int dfs(int now,int fa)
    {
    	int sz = 1;
    	memset(f[now],128,sizeof(f[now])); f[now][0] = f[now][1] = 0;
    	for (int i = side[now];i;i = next[i])
    	{
    		if (toit[i] == fa) continue;
    		int num = dfs(toit[i],now);
    		for (int j = min(K,sz += num);j >= 0;--j)
    			for (int k = 0;k <= num&&k <= j;++k)
    				upd(f[now][j],((ll)(K-k)*(ll)k+(ll)(N-K-(num-k))*(ll)(num-k))*(ll)len[i]+f[toit[i]][k]+f[now][j-k]);
    	}
    	return sz;
    }
    
    int main()
    {
    	freopen("4033.in","r",stdin);
    	freopen("4033.out","w",stdout);
    	scanf("%d %d",&N,&K);
    	for (int i = 1,a,b,c;i < N;++i)	scanf("%d %d %d",&a,&b,&c),ins(a,b,c);
    	dfs(1,0);
    	printf("%lld",f[1][K]);
    	fclose(stdin); fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    如何使用Java计算货币/钱~(How to calculate monetary values in Java)
    BigDecimal类
    状态码定义
    常见服务器返回状态码(Status Codes)
    2020-3-26学习地图
    ReentrantLock类
    HashSet类
    Vector类
    课程总结
    第十四周课程总结&实验报告
  • 原文地址:https://www.cnblogs.com/mmlz/p/5868714.html
Copyright © 2020-2023  润新知