洛谷P3177||bzoj4033 [HAOI2015]树上染色
根本不会做。。。
上网查了题解,发现只要在状态定义的时候就考虑每一条边全局的贡献就好了?
考虑边的贡献和修改状态定义我都想到了,然而并不能想到要结合起来
ans[i][j]表示i子树中选j个黑色节点,最大的贡献和
容易知道:每一条边的贡献为 长度*(边一侧的白点数*边另一侧的白点数+边一侧的黑点数*边另一侧的黑点数)
可以发现,如果已经确定一棵子树中选多少个黑点,那么这棵子树的根到其父亲的连边的贡献可以直接确定
考虑向一棵树的根节点(u)下再加入一棵子树(v)时的转移:sz[u]表示u子树的节点个数
$ans[u][k]=max\{ans[u][i]+ans[v][j]+dis(u,v)*(j*(K-j)+(sz[v]-j)*(n-sz[v]-K+j))\}(k=i+j)$
要构建一棵树,可以先构建完所有以根节点的某个子节点为根的子树,然后再依次将子树与根节点连上边。
复杂度好像是n^3的?事实上只要改一下循环的上界就n^2了。转移某个子树时,i上界为之前已经转移过的子树size和 + 1(根节点自身),j上界为目标子树size
可以发现,树上每一对点对刚好产生1次转移(在lca处产生),因此总复杂度等于总点对数是n^2的
1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #include<vector> 5 using namespace std; 6 #define fi first 7 #define se second 8 #define mp make_pair 9 #define pb push_back 10 typedef long long ll; 11 typedef unsigned long long ull; 12 struct E 13 { 14 ll to,nxt,d; 15 }e[4010]; 16 ll f1[2010],ne; 17 ll ans[2010][2010]; 18 ll sz[2010]; 19 ll tmp[2010]; 20 ll n,K; 21 void dfs(ll u,ll fa) 22 { 23 ans[u][0]=ans[u][1]=0;sz[u]=1; 24 ll i,j; 25 for(ll v,k=f1[u];k;k=e[k].nxt) 26 if(e[k].to!=fa) 27 { 28 v=e[k].to; 29 dfs(v,u); 30 memset(tmp,192,sizeof(ll)*(sz[u]+sz[v]+1)); 31 for(i=sz[u];i>=0;--i) 32 { 33 for(j=sz[v];j>=0;--j) 34 { 35 if(K>=j&&n-sz[v]-K+j>=0) 36 { 37 tmp[i+j]=max(tmp[i+j],ans[u][i]+ans[v][j] 38 +e[k].d*(j*(K-j)+(sz[v]-j)*(n-sz[v]-K+j))); 39 } 40 } 41 } 42 for(i=0;i<=sz[u]+sz[v];++i) 43 ans[u][i]=tmp[i]; 44 sz[u]+=sz[v]; 45 } 46 //printf("1t%lld\n",u); 47 //for(i=0;i<=sz[u];i++) 48 // printf("%lld %lld\n",i,ans[u][i]); 49 } 50 int main() 51 { 52 ll i,x,y,z; 53 memset(ans,192,sizeof(ans)); 54 scanf("%lld%lld",&n,&K); 55 for(i=1;i<n;++i) 56 { 57 scanf("%lld%lld%lld",&x,&y,&z); 58 e[++ne].to=y;e[ne].nxt=f1[x];f1[x]=ne;e[ne].d=z; 59 e[++ne].to=x;e[ne].nxt=f1[y];f1[y]=ne;e[ne].d=z; 60 } 61 dfs(1,0); 62 printf("%lld",ans[1][K]); 63 return 0; 64 }