【JZOJ1899】【2010集训队出题】剪枝

题目大意

给出一个有根树,\(1\)为根,若某个节点的儿子全是叶子,你可以将该节点的儿子全部剪掉,这样的操作可以进行多次。定义这棵树的价值为:将树上所有叶子按照\(dfs\)序排序后,所有叶子点权之和-相邻两叶子路径上点权最大值。现在你要通过剪枝使得这棵树价值最大。

\(n\leq 100000\)

分析

设\(f_i\)表示\(i\)作为最后一个叶子时的最大价值。暴力枚举原树(没有剪枝)相邻的两个叶子,显然左链上每个点的\(f\)都可以转移到右链上,我们暴力处理出这条路径,分类讨论点权最大值在左链还是右链,进行状态转移。可以发现,一条边被枚举的次数最多是\(2\),一次是从上个子树进入这个子树,一次是从这个子树进入下个子树,所以暴力枚举复杂度其实是\(O(n)\)的,就可以随便做了。

重点!!!!!!
暴力枚举一棵树相邻两叶子的路径复杂度是\(O(n)\)。

Code

#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 100007;
inline int read()
{
    int x = 0, f = 0;
    char c = getchar();
    for (; c < '0' || c > '9'; c = getchar()) if (c == '-') f = 1;
    for (; c >= '0' && c <= '9'; c = getchar()) x = (x << 1) + (x << 3) + (c ^ '0');
    return f ? -x : x;
}

int n, dfn, tot, ans, w[N], p[N], fa[N], dep[N], ord[N], leaf[N], f[N];
int len, arr[N], mx1[N], mx2[N];
vector<int> son[N];

void dfs(int u)
{
    ord[++dfn] = u;
    int sz = son[u].size();
    for (int i = 0; i < sz; i++) fa[son[u][i]] = u, dep[son[u][i]] = dep[u] + 1, dfs(son[u][i]);
}
int getlca(int u, int v)
{
    if (dep[u] < dep[v]) swap(u, v);
    while (dep[fa[u]] >= dep[v]) u = fa[u];
    if (u == v) return u;
    while (fa[u] != fa[v]) u = fa[u], v = fa[v];
    return fa[u];
}

int main()
{
    //freopen("cut.in", "r", stdin);
    n = read();
    for (int i = 1; i <= n; i++)
    {
        w[i] = read(), p[i] = read();
        for (int j = 1, a; j <= p[i]; j++) a = read(), son[i].push_back(a);
    }
    dep[1] = 1, dfs(1);
    for (int i = 1; i <= n; i++) if (!son[ord[i]].size()) leaf[++tot] = ord[i];
    for (int i = 1; i <= n; i++) f[i] = -0x3f3f3f3f;
    int x = leaf[1];
    while (x != 1) f[x] = w[x], x = fa[x];
    for (int i = 2; i <= tot; i++)
    {
        int a = leaf[i - 1], b = leaf[i], c = getlca(a, b), dist = dep[a] + dep[b] - 2 * dep[c] + 1;
        arr[len = 1] = a; while (fa[a] != c) a = fa[a], arr[++len] = a;
        arr[++len] = c;
        arr[dist--] = b; while (fa[b] != c) b = fa[b], arr[dist--] = b;
        mx1[len] = w[c], dist = dep[leaf[i - 1]] + dep[leaf[i]] - 2 * dep[c] + 1;
        for (int j = len - 1; j >= 1; j--) mx1[j] = max(mx1[j + 1], w[arr[j]]);
        for (int j = len + 1; j <= dist; j++) mx1[j] = max(mx1[j - 1], w[arr[j]]);
        mx2[0] = -0x3f3f3f3f; for (int j = 1; j <= len - 1; j++) mx2[j] = max(mx2[j - 1], f[arr[j]] - mx1[j + 1]);
        for (int j = len + 1, k = len, maxf = -0x3f3f3f3f; j <= dist; j++)
        {
            while (k > 1 && mx1[k] <= mx1[j - 1]) k--, maxf = max(maxf, f[arr[k]]);
            f[arr[j]] = max(f[arr[j]], maxf - mx1[j - 1] + w[arr[j]]);
            if (k > 1) f[arr[j]] = max(f[arr[j]], mx2[k - 1] + w[arr[j]]);
        }
    }
    x = leaf[tot];
    while (x != 1) ans = max(ans, f[x]), x = fa[x];
    printf("%d\n", ans);
    return 0;
}

原文地址:https://www.cnblogs.com/zjlcnblogs/p/11350864.html

时间: 2024-10-16 00:34:42

【JZOJ1899】【2010集训队出题】剪枝的相关文章

【2010集训队出题】小Z的袜子

Description 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命-- 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬. 你的任务便是告诉小Z,他有多大的概率抽到两只颜色相同的袜子.当然,小Z希望这个概率尽量高,所以他可能会询问多个(L,R)以方便自己

[JZOJ1904] 【2010集训队出题】拯救Protoss的故乡

题目 题目大意 给你一个树形的网络,每条边从父亲流向儿子.根节点为原点,叶子节点流向汇点,容量为无穷大. 可以给一些边扩大容量,最多总共扩大\(m\)容量.每条边的容量有上限. 求扩大容量后最大的最大流. 思考历程 隐隐约约地猜到正解跟树链剖分有什么关系,可是没有打,也没有时间打. 只能暴力DP来水分. 设\(h_{i,j}\)为\(i\)的父亲到\(i\)的最大流,扩大了\(j\)次容量.\(g_{i,j}\)为\(i\)到子树的最大流,扩大了\(j\)次容量.前者由后者和边的容量取最小值后得

[JZOJ1901] 【2010集训队出题】光棱坦克

题目 题目大意 给你个平面上的一堆点,问序列\({p_i}\)的个数. 满足\(y_{p_{i-1}}>y_{p_i}\)并且\(x_{p_i}\)在\(x_{p_i-1}\)和\(x_{p_i-2}\)之间. 正解 我不知道为什么我的树状数组打挂了--尽管不一定能AC,但是WA了-- 这题的正解有很多,最为传奇的,则是彭大爷的神仙解法. 显然这是个DP,而他抛弃了按照\(y\)从大到小排序的传统做法,反而是以\(x\)从小到大排序.将\({p_i}\)倒过来做.设\(f_{i,0/1}\)表示

JZOJ 1981. 【2011集训队出题】Digit

JZOJ 1981. [2011集训队出题]Digit Time Limits: 1000 ms Memory Limits: 128000 KB Description 在数学课上,小T又被老师发现上课睡觉了.为了向全班同学证明小T刚才没有好好听课,数学老师决定出一道题目刁难一下小T,如果小T答不出,那么-- 情节就按照俗套的路线发展下去了,小T显然无法解决这么复杂的问题,可怜的小T只能向你求助: 题目是这样的: 求一个满足条件的n位数A(不能有前导0),满足它的数字和为s1,并且,A*d的数

【JZOJ1914】【2011集训队出题】最短路

题目大意 给你一个带权无向图,满足图上任意一条边最多属于一个环,有\(q\)个询问,求\(u,v\)之间的最短路. \(n,q\leq 10000\) Solution 首先用Tarjan建一棵以\(1\)为根的搜索树,找出每个环,记录环的总长,将环内每个点\(u\)连向环内\(dfs\)序最小的点\(v\),边权为\(u\)到\(v\)的最短路,然后把不在环上的边照旧连上,这样我们就得到了一棵树. 现在要求\(a\)到\(b\)的最短路,我们考虑倍增,若两个点在一条链上,它们的最短路就是树上的

圆方树小结

圆方树 jzoj 1914. [2011集训队出题]最短路 这是道圆方树+倍增LCA裸题. 圆方树,顾名思义,就是圆点和方点所组成的树. 而方点就是一个圆的根,一般都是\(dfs\)时第一个到这个圆的那个位置,然后另附一个点当做方点.然后圆所组成的点都连向方点. 而对于这种圆方边的边权,则为它到根的最近值. 从而将一个仙人掌转成了一棵树. 然后对于这棵树,我们就可以用倍增来求出两两点之间的最短路了. 注意的是,对于最后走到的位置,我们要看看它是从上面绕近还是直接从下面走更优!!! 就这样子了.

[国家集训队2010]小Z的袜子

★★★   输入文件:hose.in   输出文件:hose.out   简单对比 时间限制:1 s   内存限制:512 MB [题目描述] 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,毕竟穿两只不同色的袜子会很尴尬. 你的任务便

洛谷 1775. [国家集训队2010]小Z的袜子

1775. [国家集训队2010]小Z的袜子 ★★★   输入文件:hose.in   输出文件:hose.out   简单对比时间限制:1 s   内存限制:512 MB [题目描述] 作为一个生活散漫的人,小Z每天早上都要耗费很久从一堆五颜六色的袜子中找出一双来穿.终于有一天,小Z再也无法忍受这恼人的找袜子过程,于是他决定听天由命…… 具体来说,小Z把这N只袜子从1到N编号,然后从编号L到R(L 尽管小Z并不在意两只袜子是不是完整的一双,甚至不在意两只袜子是否一左一右,他却很在意袜子的颜色,

AC日记——[国家集训队2010]小Z的袜子 cogs 1775

[国家集训队2010]小Z的袜子 思路: 传说中的莫队算法(优雅的暴力): 莫队算法是一个离线的区间询问算法: 如果我们知道[l,r], 那么,我们就能O(1)的时间求出(l-1,r),(l+1,r),(l,r-1),(l,r+1); 莫队算法怎么保证时间呢? 把询问排序; 然后进行暴力; 但是这样仍然需要很长很长的时间; 所以,我们引入一个根号方法,分块; 把区间的点分块; 然后每个询问的l,r按l所属的块为第一关键字,l,r为第二第三; 排序完后,就可以保证复杂度是O(n*sqrt(n));