bzoj1812 [IOI2005]riv河流

题目链接

problem

给出一棵树,每个点有点权,每条边有边权。0号点为根,每个点的代价是这个点的点权\(\times\)该点到根路径上的边权和。
现在可以选择最多K个点。使得每个点的代价变为:这个点的点权\(\times\)改点到最近的被选中的一个祖先的边权和。
问所有点的代价和最小为多少。

solution

用\(g[i][j]\)表示以i为根的子树,强制选i,最大的贡献(这里的贡献是指比什么也不选所减少的代价。)

最终答案肯定就是初始代价-g[0][k]

考虑怎么维护出\(g\)。用\(f[i][j]\)表示以\(i\)为根的子树,\(i\)可选可不选。然后树形背包一下就可以求出g。

考虑怎么维护f。每当枚举到一个根的时候,就重新dfs一遍这棵子树,初始f[x][0]=w[x]*dep[u]。dep[u]表示从枚举的根到0号点的距离。然后同样方法背包一遍,就可以维护处\(f\)。

把j写成k调了一上午。。。。

code

/*
* @Author: wxyww
* @Date:   2019-12-21 10:08:12
* @Last Modified time: 2019-12-21 11:04:12
*/
#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<ctime>
using namespace std;
typedef long long ll;
const int N = 110;
ll read() {
    ll x = 0,f = 1;char c = getchar();
    while(c < '0' || c > '9') {
        if(c == '-') f = -1; c = getchar();
    }
    while(c >= '0' && c <= '9') {
        x = x * 10 + c - '0'; c = getchar();
    }
    return x * f;
}
int siz[N],f[N][55],g[N][N],dep[N],w[N],n,K;
struct node {
    int v,nxt;
}e[N];
int head[N],ejs;
void add(int u,int v) {
    e[++ejs].v = v;e[ejs].nxt = head[u];head[u] = ejs;
}
void dp(int u,int W) {
    f[u][0] = W * w[u];
    for(int i = head[u];i;i = e[i].nxt) {
        int v = e[i].v;
        dp(v,W);
        for(int j = min(K,siz[u]);j >= 0;--j) {
            for(int k = 0;k <= min(j,siz[v]);++k) {
                f[u][j] = max(f[u][j],f[v][k] + f[u][j - k]);
            }
        }
    }
    for(int i = 1;i <= K;++i) f[u][i] = max(f[u][i],g[u][i]);//在算上强制选的答案
}
void dfs(int u) {
    siz[u] = 1;
    for(int i = head[u];i;i = e[i].nxt) {
        dep[e[i].v] += dep[u];
        dfs(e[i].v);
        siz[u] += siz[e[i].v];
    }

    g[u][1] = dep[u] * w[u];

    memset(f,0,sizeof(f));

    for(int i = head[u];i;i = e[i].nxt) {
        int v = e[i].v;
        dp(v,dep[u]);
        for(int j = min(K,siz[u]);j >= 1;--j) {
            for(int k = 0;k < j;++k) {
                g[u][j] = max(g[u][j],g[u][j - k] + f[v][k]);
            }
        }
    }
    // if(u == 1) cout<<g[1][1]<<endl;
}
int main() {
    // freopen("1.in","r",stdin);
    n = read(),K = read();
    ++K;
    for(int i = 1;i <= n;++i) {
        w[i] = read();int u = read();add(u,i);
        dep[i] = read();
    }
    dfs(0);
    int ans = 0;
    for(int i = 1;i <= K;++i) ans = max(ans,g[0][i]);
    // cout<<g[2][1];
    for(int i = 1;i <= n;++i) ans -= dep[i] * w[i];
    cout<<-ans<<endl;
    return 0;
}

原文地址:https://www.cnblogs.com/wxyww/p/bzoj1812.html

时间: 2024-11-05 20:35:06

bzoj1812 [IOI2005]riv河流的相关文章

BZOJ1812: [Ioi2005]riv

1812: [Ioi2005]riv Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 523  Solved: 309[Submit][Status][Discuss] Description 几 乎整个Byteland王国都被森林和河流所覆盖.小点的河汇聚到一起,形成了稍大点的河.就这样,所有的河水都汇聚并流进了一条大河,最后这条大河流进 了大海.这条大河的入海口处有一个村庄——名叫Bytetown 在Byteland国,有n个伐木的村庄,这些村庄

[IOI2005]Riv 河流

题目描述 几乎整个Byteland 王国都被森林和河流所覆盖.小点的河汇聚到一起,形成了稍大点的河.就这样,所有的河水都汇聚并流进了一条大河,最后这条大河流进了大海.这条大河的入海口处有一个村庄--Bytetown.在Byteland国,有n个伐木的村庄,这些村庄都座落在河边.目前在Bytetown,有一个巨大的伐木场,它处理着全国砍下的所有木料.木料被砍下后,顺着河流而被运到Bytetown的伐木场.Byteland 的国王决定,为了减少运输木料的费用,再额外地建造k个伐木场.这k个伐木场将被

1812: [Ioi2005]riv

1812: [Ioi2005]riv Time Limit: 10 Sec Memory Limit: 64 MB Submit: 635 Solved: 388 [Submit][Status][Discuss] Description 几乎整个Byteland王国都被森林和河流所覆盖.小点的河汇聚到一起,形成了稍大点的河.就这样,所有的河水都汇聚并流进了一条大河,最后这条大河流进了大海.这条大河的入海口处有一个村庄--名叫Bytetown 在Byteland国,有n个伐木的村庄,这些村庄都座

【BZOJ1812】riv(多叉树转二叉树,树形DP)

题意:给定一棵树,每个点有权值,每条边有边权(单向边).你可以选取K个黑点,使得从每个点移动到距离他最近的黑点的花费(距离*点权)的总和最小. n<=100 k<=50 w[i],a[i]<=10000 思路:见IOI2005龙凡解题报告 又是一道从父亲到儿子的树形DP 为什么要多叉转二叉?因为假设点U被选,这个被选点只会对U自己的儿子有影响,对U的兄弟并没有影响 dp[i,j,k]表示以i为根的子树,建j个节点,离i最近的被选点是k时的最小总和 \[ dp[i,j,k]=min\beg

IOI 2005/bzoj 1812:riv 河流

Description 几乎整个Byteland王国都被森林和河流所覆盖.小点的河汇聚到一起,形成了稍大点的河.就这样,所有的河水都汇聚并流进了一条大河,最后这条大河流进了大海.这条大河的入海口处有一个村庄--名叫Bytetown 在Byteland国,有n个伐木的村庄,这些村庄都座落在河边.目前在Bytetown,有一个巨大的伐木场,它处理着全国砍下的所有木料.木料被砍下后,顺着河流而被运到Bytetown的伐木场.Byteland的国王决定,为了减少运输木料的费用,再额外地建造k个伐木场.这

BZOJ1812 [IOI2005]river

传送门: 很常规的一道树规,转为左儿子右兄弟. 然后f[node][anc][K]表示在node节点上,最近的有贡献祖先在anc上,在node的儿子和兄弟上有k个有贡献节点的最优值. 然后得出以下转移方程. f[node][anc][K]=min{f[son[node]][anc][k]+f[bro[node]][anc][K-k]}+Value[node]*(dis[node]-dis[anc]); 无贡献 f[node][anc][K]=min{f[son[node]][node][k]+f

带权二分

带权二分 一种二分答案的套路,又叫做DP凸优化,wqs二分. 用来解决一类题目,要求某个要求出现K次,并且,可以很显然的发现,在改变相应权值的时候,对应出现的次数具有单调性.而且很显然,这种题一般满足一定的要求.而且一般权值为整数二分就可以,但是有的题需要实数二分...而且,边界条件通常很麻烦,调起来想摔电脑. 例题时间: BZOJ2654: tree 题目大意:给你一个图,里面有白色边和黑色边,问恰好有k条边白色边的最小生成树 直接贪心法肯定是错误的,因此,我们考虑带权二分. 给定一个选择白色

YCB 的暑期计划

前言 YCB现在很弱(TAT) 暑假有一个月,赶快狂补一下. 大概的计划如下: 首先前期会以数据结构为主,毕竟代码能力太弱,涉及内容:线段树分治.二进制分组.KD-Tree. 等数据结构做到没有智商的时候加入一波数论,内容为 杜教筛.min_25筛. 然后中途小清新一下,做一些 组合博弈与构造题. 接着继续练代码能力,顺便学一些神奇的暴力:启发式合并.dsu on tree . 然后图论也忘的差不多了,就回过头去学点新东西,大概会有spfa判负环.0/1分数规划.差分约束. 估计这个时候也没有什

[IOI2005]河流

Description Luogu3354 Solution 一道树形dp的题. 首先考虑转移,很简单,就是这个点做不做伐木场.为了方便转移,我们定义状态为\(f_{i,j,k}\)表示点\(i\)及其兄弟的子树中,选了\(k\)个伐木场,且\(j\)是点\(i\)的父亲中距离点\(i\)最近的那个伐木场,这时的总花费. 转移就比较好写了: \[ f_{i,j,k} = max\{ f_{son_i, j, l} + f_{bro_i, j, k-l} + w_i * dis_{i, j} \}