标签:def namespace can stream 大小 pen 最优 type mic
考虑每条边对不同染色方案的贡献,用树包的方式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;
}
标签:def namespace can stream 大小 pen 最优 type mic
原文地址:https://www.cnblogs.com/qjs12/p/8856069.html