• [权值线段树][DP]JZOJ 3338 法法塔的奖励


    Description

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

    Input

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

    Output

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

    Sample Input

    输入1:
    2
    -1 1
    1 1

    输入2:
    6
    -1 1 2 1 2 4
    4 3 4 3 6 2
     

    Sample Output

    输出1:
    2 1

    输出2:
    3 1 1 2 1 1
     
     

    Data Constraint

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

    分析

    先考虑朴素的做法,就是从1..i-1中权值小于等于wi的f数组中提出最大的转移上来

    那么在本题中就是在子树中权值小于等于wi的f数组中提出最大的转移上来

    然后权值线段树合并瞎搞即可

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    #include <vector>
    using namespace std;
    const int N=1e5+10;
    struct Graph {
        int v,nx;
    }g[N];
    int cnt,list[N];
    int f[N],root[N],w[N];
    int t[40*N],L[40*N],R[40*N],tcnt;
    int n;
    
    int Merge(int x,int y) {
        if (!x&&!y) return 0;
        if (!x) return y;
        if (!y) return x;
        int T=++tcnt;
        t[T]=max(t[x],t[y]);
        L[T]=Merge(L[x],L[y]);
        R[T]=Merge(R[x],R[y]);
        return T;
    }
    
    void Insert(int &x,int l,int r,int k,int z) {
        if (!x) x=++tcnt;
        if (l==r&&r==k) {
            t[x]=max(t[x],z);
            return;
        }
        int mid=l+r>>1;
        if (k<=mid) Insert(L[x],l,mid,k,z);
        else Insert(R[x],mid+1,r,k,z);
        t[x]=max(t[L[x]],t[R[x]]);
    }
    
    int Query(int x,int l,int r,int ll,int rr) {
        if (rr<l||r<ll||r<l) return 0;
        if (!x) return 0;
        if (ll<=l&&r<=rr) return t[x];
        int mid=l+r>>1,c1=0,c2=0;
        if (ll<=mid) c1=Query(L[x],l,mid,ll,rr);
        if (mid<rr) c2=Query(R[x],mid+1,r,ll,rr);
        return max(c1,c2);
    }
    
    void DFS(int u) {
        for (int i=list[u];i;i=g[i].nx) {
            DFS(g[i].v);
            root[u]=Merge(root[u],root[g[i].v]);
        }
        f[u]=Query(root[u],1,n,1,w[u])+1;
        Insert(root[u],1,n,w[u],f[u]);
    }
    
    int main() {
        scanf("%d",&n);
        for (int i=1,f;i<=n;i++) {
            scanf("%d",&f);
            if (f>0) g[++cnt]=(Graph){i,list[f]},list[f]=cnt;
        }
        for (int i=1;i<=n;i++) scanf("%d",&w[i]);
        DFS(1);
        for (int i=1;i<=n;i++) printf("%d ",f[i]);
    }
    View Code
    在日渐沉没的世界里,我发现了你。
  • 相关阅读:
    初学JAVA随记——代码练习(输出半个菱形 for语句嵌套)
    初学JAVA随记——代码练习(体重问题,含switch、if else、三元条件运算符)
    初学JAVA随记——代码练习(二元一次方程)
    初学JAVA——语句的几个要点
    初学JAVA随记——运算符的几个要点2
    初学JAVA——试写if条件代码(自身体重为例)
    初学JAVA——运算符的几个要点
    初学JAVA——栈空间堆空间的理解
    自制刻度尺插件-前端简易实现"腾讯信用"界面
    JavaScript快速查找节点
  • 原文地址:https://www.cnblogs.com/mastervan/p/11166686.html
Copyright © 2020-2023  润新知