[HAOI2015]树上染色
Posted qjs12
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[HAOI2015]树上染色相关的知识,希望对你有一定的参考价值。
考虑每条边对不同染色方案的贡献,用树包的方式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; }
以上是关于[HAOI2015]树上染色的主要内容,如果未能解决你的问题,请参考以下文章