树上染色 bzoj-4033 HAOI-2015
题目大意:给定一棵n个点的树,让你在其中选出k个作为黑点,其余的是白点,收益为任意两个同色点之间距离的和。求最大收益。
注释:$1le nle 2000$
想法:我们看到了数据范围...一般树上问题这个数据范围一般就是背包或者数据结构,这题我们考虑树上背包。
我们考虑枚举每一棵子树选取一些黑点的贡献。但是这样选取是有后效性的,因为内部点的选取可能在外面选取同样的点产生不一样的效果,所以我们尝试把后效性移除。
具体地:我们可以将边权下传到点权,之后所有的关于每条边脑袋上的那条边的权值都预先被更新就没有后效性了。
最后,附上丑陋的代码... ...
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define N 2010 using namespace std; typedef long long ll; int n,m; ll f[N][N],dpth[N]; int size[N]; int head[N],to[N<<1],nxt[N<<1],val[N<<1],tot; inline char nc() {static char *p1,*p2,buf[100000]; return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++;} int rd() {int x=0; char c=nc(); while(!isdigit(c)) c=nc(); while(isdigit(c)) x=(x<<3)+(x<<1)+c-'0',c=nc(); return x;} inline void add(int x,int y,int z) {to[++tot]=y; val[tot]=z; nxt[tot]=head[x]; head[x]=tot;} void dfs(int pos,int fa) { size[pos]=1; for(int i=head[pos];i;i=nxt[i]) if(to[i]!=fa) { dpth[to[i]]=val[i]; dfs(to[i],pos); for(int j=min(m,size[pos]);~j;j--) for(int k=min(size[to[i]],m-j);~k;k--) f[pos][j+k]=max(f[pos][j+k],f[pos][j]+f[to[i]][k]); size[pos]+=size[to[i]]; } for(int i=0;i<=min(m,size[pos]);i++) f[pos][i]+=dpth[pos]*((i*(m-i))+(size[pos]-i)*(n-size[pos]-m+i)); } void test() { puts("size"); for(int i=1;i<=n;i++) printf("%d ",size[i]); puts(""); puts("f"); for(int i=1;i<=n;i++) {for(int j=0;j<=m;j++) printf("%lld ",f[i][j]); puts("");} } int main() { n=rd(),m=rd(); int x,y,z; for(int i=1;i<n;i++) {x=rd(),y=rd(),z=rd(); add(x,y,z); add(y,x,z);} dfs(1,1);/* test(); */ printf("%lld ",f[1][m]); } /* 5 2 1 2 3 1 5 1 2 3 1 2 4 2 */
小结:嘻嘻嘻嘻,好东西。