【线段树】【FeyatCup】——2.法法塔的奖励

首先感谢并膜拜两位大佬 wyl8899 & 法法塔 感谢两位的出题!

题目:


法法塔是很喜欢写程序的。所以冒着就算码农屌丝一辈子的风险也大无畏地写着程序。
码农们为了表彰法法塔的坚持与执着,决定给法法塔颁布奖励,为了体现码农的屌丝气质,奖励也将由法法塔自己做出选择!
所有的奖励被组织成了一棵树的形态,每个点都有一个权值。法法塔首先选择一个子树,然后选择从该子树内的一个叶子节点到该子树的根的一条路径,将路径上节点的权值依次排成一个序列,求得这个序列的最长不下降子序列长度,这个长度就是他能得到的奖品数目。要求该子树的根必须被选择。
接下来就是法法塔进行选择的时间了。尽可能多地得到奖品是自然的。那么法法塔到底能够得到多少奖品呢?这是个很纠结的问题,他决定交给你来解决。对于每个子树,他都希望你能求出他首先选择了这个子树后,他能得到的最多的奖品数目。

输入数据:

第一行一个数n,描述树上节点的个数。
接下来一行n个数,描述树上每个点的父亲,点1为根,其父亲设为-1,其余每个点的父亲的标号都小于自己。 
接下来一行n个数,表示树上每个点的权值。

输出数据:

一行n个数,第i个数表示法法塔选择了以第i个节点为根的子树后,他可以获得的最多的奖品个数。

数据范围:

n<=100000,每个数的权值都是1到n的正整数。

  拆分题意,这道题是求树上求最长不下降子序列,但是如果将每一条边单独拿出来进行dp是会爆空间的,所以我们考虑别的方法。



参考:

https://www.cnblogs.com/chhokmah/p/10550155.html

https://www.cnblogs.com/Mychael/p/8665589.html

https://www.luogu.org/blog/wcr20020327/xin-ren-di-di-yi-ci-blog-xian-duan-shu

动态开点线段树&&线段树合并:

  顾名思义,动态开点线段树就是在线段树的基础上实现动态开点,达到减小空间的方法。与原来的线段树有一点不同,动态开点线段树拥有左儿子(ls)与右儿子(rs),便于进行线段树与点之间的组合和更新。

  举个例子,单点构造是在原有线段树上开启新的点(要注意取地址符的使用):

 1 void change(int &o,int l,int r,int a,int b){//a=pos,b=size
 2     if (!o) o=++cnt;
 3     if (l==r){
 4         g[o].sum=max(g[o].sum,b);
 5         return;
 6     }
 7     int mid=(l+r)/2;
 8     if (a<=mid) change(g[o].ls,l,mid,a,b);
 9     else change(g[o].rs,mid+1,r,a,b);
10     g[o].sum=max(g[g[o].ls].sum,g[g[o].rs].sum);
11 }

其余的不再赘述,而线段树合并则是将两个线段树合并在一起(废话),参见代码:

 1 //Mychael大大的代码
 2 int merge(int u,int v){
 3     if (!u) return v;//如果没有u点,则返回v做根
 4     if (!v) return u;//类似上面
 5     int t = ++cnt;//如果两边都有,就新建t点作为根
 6     sum[t] = sum[u] + sum[v];//融合两个点,但是在这个程序中需要改为更新最大值
 7     ls[t] = merge(ls[u],ls[v]);//递归修改
 8     rs[t] = merge(rs[u],rs[v]);
 9     return t;
10 }

经过上面两个操作,这道题的解法应该已经大致明晰了呢:

我们在一个子树中找到当前最长不下降子序列中的最大值,并在其基础上加1即可。

//continue
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10;
int head[N],cnt,num[N],rt[N],n,ans[N];
struct edge{
    int to,next;
}e[N];
struct line{
    int ls,rs,sum;
}tr[N<<1];
void addedge(int from,int to){
    e[++cnt].to=to;
    e[cnt].next=head[from];
    head[from]=cnt;
}
void change(int &o,int l,int r,int siz,int pos){ //动态开点
    if(!o) o=++cnt;
    if(l==r){
        tr[o].sum=max(tr[o].sum,siz);
        return;
    }
    int mid=(l+r)>>1;
    if(pos<=mid) change(tr[o].ls,l,mid,siz,pos);
    else change(tr[o].rs,mid+1,r,siz,pos);
    tr[o].sum=max(tr[tr[o].ls].sum,tr[tr[o].rs].sum);
}
int query(int o,int l,int r,int a,int b){//查询区间最大值
    if(!o) return 0;
    if(l==a&&r==b) return tr[o].sum;
    int mid=(l+r)>>1;
    if(b<=mid) return query(tr[o].ls,l,mid,a,b);
    else if(a>mid) return query(tr[o].rs,mid+1,r,a,b);
    else return max(query(tr[o].ls,l,mid,a,mid),query(tr[o].rs,mid+1,r,mid+1,b));
}
int merge(int a,int b,int l,int r){
    if(!a||!b) return a+b;
    if(l==r){
        tr[a].sum=max(tr[a].sum,tr[b].sum);
        return a;
    }
    int mid=(l+r)>>1;
    tr[a].ls=merge(tr[a].ls,tr[b].ls,l,mid);
    tr[a].rs=merge(tr[a].rs,tr[b].rs,mid+1,r);
    tr[a].sum=max(tr[a].sum,tr[b].sum);
    return a;
}
void dfs(int o){//遍历整个树,将线段树合并求最大值
    int j=0;
    for(int u=head[o];u;u=e[u].next){
        int v=e[u].to;
        dfs(v);
        j=merge(j,rt[v],1,n);
    }
    ans[o]=query(j,1,n,1,num[o])+1;
    change(j,1,n,ans[o],num[o]);
    rt[o]=j;
}
int main(){
    scanf("%d",&n);
    int fa;
    for(int i=1;i<=n;i++){
        scanf("%d",&fa);
        if(fa!=-1)
        addedge(fa,i);
    }
    for(int i=1;i<=n;i++) scanf("%d",&num[i]);
    cnt=0;
    dfs(1);
    for(int i=1;i<=n;i++) printf("%d ",ans[i]);
    return 0;
}
 

原文地址:https://www.cnblogs.com/Nelson992770019/p/11160434.html

时间: 2024-10-17 05:23:28

【线段树】【FeyatCup】——2.法法塔的奖励的相关文章

Vijos P1448 校门外的树【多解,线段树,树状数组,括号序列法+暴力优化】

校门外的树 描述 校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的…… 如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作: K=1,K=1,读入l.r表示在区间[l,r]中种上一种树,每次操作种的树的种类都不同 K=2,读入l,r表示询问l~r之间能见到多少种树 (l,r>0) 格式 输入格式 第一行n,m表示道路总长为n,共有m个操作 接下来m行为m个操作 输出格式 对于每个k=2输出一个答案 样例1 样例输入1 5 4 1 1

Vijos1448题解---线段树+括号法

描述 校门外有很多树,有苹果树,香蕉树,有会扔石头的,有可以吃掉补充体力的……如今学校决定在某个时刻在某一段种上一种树,保证任一时刻不会出现两段相同种类的树,现有两个操作:K=1,K=1,读入l.r表示在区间[l,r]中种上一种树,每次操作种的树的种类都不同K=2,读入l,r表示询问l~r之间能见到多少种树(l,r>0) 输入格式 第一行n,m表示道路总长为n,共有m个操作接下来m行为m个操作 输出格式 对于每个k=2输出一个答案 样例输入 5 4 1 1 3 2 2 5 1 2 4 2 3 5

POJ 1635 树的最小表示法/HASH

题目链接:http://poj.org/problem?id=1635 题意:给定两个由01组成的串,0代表远离根,1代表接近根.相当于每个串对应一个有根的树.然后让你判断2个串构成的树是否是同构的. 思路:首先根据01串构造出树,然后求树的最小表示法判断同构. 详情参照:https://www.byvoid.com/blog/directed-tree-bracket-sequence/ #define _CRT_SECURE_NO_DEPRECATE #include<iostream>

2014多校1011--hdu--4097--Killing Monsters(塔防,线段树超时。。)

Killing Monsters Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 107    Accepted Submission(s): 54 Problem Description Kingdom Rush is a popular TD game, in which you should build some towers

javascript实现数据结构: 树和二叉树的应用--最优二叉树(赫夫曼树),回溯法与树的遍历--求集合幂集及八皇后问题

赫夫曼树及其应用 赫夫曼(Huffman)树又称最优树,是一类带权路径长度最短的树,有着广泛的应用. 最优二叉树(Huffman树) 1 基本概念 ① 结点路径:从树中一个结点到另一个结点的之间的分支构成这两个结点之间的路径. ② 路径长度:结点路径上的分支数目称为路径长度. ③ 树的路径长度:从树根到每一个结点的路径长度之和. 以下图为例: A到F :结点路径 AEF : 路径长度(即边的数目) 2 : 树的路径长度:3*1+5*2+2*3=19: ④ 结点的带权路径长度:从该结点的到树的根结

看数据结构写代码(59) 键树的双链表示法

杂谈; 打败自己的 往往不是敌人,而是自己.坚持不易,且行且珍惜. 键树又称单词查找树,Trie树,是一种树形结构,是一种哈希树的变种.典型应用是用于统计,排序和保存大量的字符串(但不仅限于字符串),所以经常被搜索引擎系统用于文本词频统计.它的优点是:利用字符串的公共前缀来减少查询时间,最大限度地减少无谓的字符串比较,查询效率比哈希树高 键树的根节点不包含字符,除根节点外的非叶子节点都只包含一个字符,叶子节点存储具体信息.: 从根节点到某一节点,路径上经过的字符连接起来,为该节点对应的字符串:

讲解——线段树

   讲解--线段树 O.引例 A.给出n个数,n<=100,和m个询问,每次询问区间[l,r]的和,并输出. 一种回答:这也太简单了,O(n)枚举搜索就行了. 另一种回答:还用得着o(n)枚举,前缀和o(1)就搞定. 那好,我再修改一下题目. B.给出n个数,n<=100,和m个操作,每个操作可能有两种:1.在某个位置加上一个数:2.询问区间[l,r]的和,并输出. 回答:o(n)枚举. 动态修改最起码不能用静态的前缀和做了. 好,我再修改题目: C.给出n个数,n<=1000000,

NOIP2012借教室[线段树|离线 差分 二分答案]

题目描述 在大学期间,经常需要租借教室.大到院系举办活动,小到学习小组自习讨论,都需要 向学校申请借教室.教室的大小功能不同,借教室人的身份不同,借教室的手续也不一样. 面对海量租借教室的信息,我们自然希望编程解决这个问题. 我们需要处理接下来n天的借教室信息,其中第i天学校有ri个教室可供租借.共有m份 订单,每份订单用三个正整数描述,分别为dj,sj,tj,表示某租借者需要从第sj天到第tj天租 借教室(包括第sj天和第tj天),每天需要租借dj个教室. 我们假定,租借者对教室的大小.地点没

POJ 3667 线段树区间合并

http://www.cnblogs.com/scau20110726/archive/2013/05/07/3065418.html 用线段树,首先要定义好线段树的节点信息,一般看到一个问题,很难很快能确定线段树要记录的信息做线段树不能为了做题而做,首先线段树是一种辅助结构,它是为问题而生的,因而必须具体问题具体分析回忆一下RMQ问题,其实解决RMQ有很多方法,根本不需要用到线段树,用线段树解决RMQ,其实是利用线段树的性质来辅助解决这个问题回忆一下求矩形面积并或周长并的问题,一般使用的是扫描