zoukankan      html  css  js  c++  java
  • 【HAOI2015】树上染色

    链接

    看到题想了想,或许可以算每个子树,就像普通树形dp.

    然后搞出了 dp [ i ] [ j ] ,g [ i ] [ j ] ,cnt [ i ] [ j ]...

    并且我可以严谨的证明这玩意是错的。

    然后移步题解,意识到树性DP除了可以按点统计,还可以按边来看。

    考虑枚举到一条边 ( u , v ) ,对答案的贡献就是 black [ u ] * black [ v ] + white [ u ] * white [ v ] * w [ i ].z;

    很自然,我们设 dp [ i ] [ j ] 为以 i 为根的子树内有 j 个黑节点的最大收益。

    简单转移一下就好。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<string>
     4 #include<cstring>
     5 using namespace std;
     6 const int N=2005;
     7 typedef long long LL;
     8 
     9 inline LL read(){
    10     LL x=0,f=1;char ch=getchar();
    11     while(ch<'0'||ch>'9'){
    12         if(ch=='-')f=-1;
    13         ch=getchar();
    14     }
    15     while(ch>='0'&&ch<='9')x=x*10+ch-'0',ch=getchar();
    16     return x*f;
    17 }
    18 
    19 int n,kk;
    20 LL x,y,z;
    21 struct node{
    22     LL y,nxt,z;
    23 }e[N<<1];
    24 int tot=0,h[N];
    25 
    26 inline void ad(LL x,LL y,LL z){
    27     ++tot;
    28     e[tot].y=y;e[tot].z=z;e[tot].nxt=h[x];h[x]=tot;
    29 }
    30 
    31 LL dp[N][N];
    32 int sz[N];
    33 
    34 inline void dfs(int x,int f){
    35     sz[x]=1;
    36     for(int i=h[x];i;i=e[i].nxt){
    37         int y=e[i].y;
    38         if(y==f)continue;
    39         dfs(y,x);sz[x]+=sz[y];
    40     }
    41 }
    42 
    43 inline void solve(int x,int f){
    44     memset(dp[x],-1,sizeof(dp[x]));dp[x][0]=dp[x][1]=0;
    45     for(int i=h[x];i;i=e[i].nxt){
    46         int y=e[i].y;
    47         if(y==f)continue;
    48         solve(y,x);
    49         for(int j=min(kk,sz[x]);j>=0;--j){
    50             for(int k=0;k<=min(j,sz[y]);++k){
    51                 if(~dp[x][j-k]){
    52                     LL val=((kk-k)*k+(n-sz[y]-(kk-k))*(sz[y]-k))*e[i].z;
    53                     dp[x][j]=max(dp[x][j],dp[y][k]+dp[x][j-k]+val);
    54                 }
    55             }
    56         }
    57     }
    58 }
    59 int main(){
    60     n=read();kk=read();
    61     for(int i=1;i<n;++i){
    62         x=read();y=read();z=read();
    63         ad(x,y,z);ad(y,x,z);
    64     }
    65     dfs(1,1);
    66     solve(1,1);
    67     cout<<dp[1][kk];
    68 } 

    这里的初值为什么要设为-1?

    因为转移是倒序的,所以第一次转移时可能发生dp[x][j-k]无值可传的现象,这样统计的答案就不是实际的值了。

    做DP问题要关注状态是否真实,真实就是指合法的从已知状态转移过来。

    转了假状态,会使答案变得fake。

  • 相关阅读:
    【ZJ选讲·压缩】
    【ZJ选讲·调整】
    【ZJ选讲·钻石游戏】
    【ZJ选讲·BZOJ 5073】
    【ZJ选讲·BZOJ 5071】
    【模拟赛·polyline】
    【BZOJ 2744 朋友圈】
    【BZOJ 2241 打地鼠】
    【CF edu 30 D. Merge Sort】
    【CF edu 30 C. Strange Game On Matrix】
  • 原文地址:https://www.cnblogs.com/chiyo/p/11289254.html
Copyright © 2011-2022 走看看