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]);
}