• 洛谷P3177||bzoj4033 [HAOI2015]树上染色


    洛谷P3177

    bzoj4033

    根本不会做。。。

    上网查了题解,发现只要在状态定义的时候就考虑每一条边全局的贡献就好了?

    考虑边的贡献和修改状态定义我都想到了,然而并不能想到要结合起来

    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
    ",u);
    47     //for(i=0;i<=sz[u];i++)
    48     //    printf("%lld %lld
    ",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 }
    View Code
  • 相关阅读:
    Linux常用操作命令大全
    判断系统,是否是移动端
    一款兼容小程序和web端的框架 Kbone-API
    defer 和 async的区别
    快应用多次弹出添加到桌面的bug
    js判断一个元素是否在数组中
    css 画太极
    css3锯齿
    js 获取屏幕宽度
    js 判断div距离浏览器顶部或者底部的距离
  • 原文地址:https://www.cnblogs.com/hehe54321/p/9846662.html
Copyright © 2020-2023  润新知