JZOJ5609. 【NOI2018模拟3.28】Tree

87 篇文章 0 订阅

Description

给你一颗有 n 个点的树,其中 1 号点为根节点,每个点都有一个权值 vali
你可以从树中选择一些点,注意如果 i 与 j 都被选中且 j 在 i 的子树内,那么必须满足 vali >valj
请你求出最多能同时选出多少个点

Input

第一行一个正整数 n
下接 n 行,每行两个正整数 vali ,fi ,其中 vali 表示该点权值,fi 表示这个结点的父亲结点
f1 = 0;∀2 ≤ i ≤ n,fi < i

Output

一行一个整数,表示答案

Sample Input

6
3 0
1 1
2 1
3 1
4 1
5 1

Sample Output

5

Data Constraint

对于 30% 的数据满足 n ≤ 2000
对于另 20% 的数据满足 fi = i − 1
对于 100% 的数据满足 n ≤ 100000,f1 = 0,∀2 ≤ i ≤ n,fi < i,vali ≤ 10^9

题解

考虑一条链的情况,
就是求从末尾开始的最长上升序列。
类似的,推广到树上,
fi f i 长度为i的最长上升子序列的结尾最小是什么。
对于每一个点,用multiset来维护这个f,
启发式合并。

code

#include <queue>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#include <time.h>
#include <set> 
#define ll long long
#define N 100003
#define M 103
#define db double
#define P putchar
#define G getchar
#define inf 998244353
#define pi 3.1415926535897932384626433832795
using namespace std;
char ch;
void read(int &n)
{
    n=0;
    ch=G();
    while((ch<'0' || ch>'9') && ch!='-')ch=G();
    ll w=1;
    if(ch=='-')w=-1,ch=G();
    while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
    n*=w;
}

int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
ll abs(ll x){return x<0?-x:x;}
ll sqr(ll x){return x*x;}
void write(ll x){if(x>9) write(x/10);P(x%10+'0');}

int n,x,y,nxt[N],to[N],lst[N],tot,v[N];
multiset <int> q[N];
multiset<int>::iterator p;

void dfs(int x)
{
    for(int i=lst[x];i;i=nxt[i])
    {
        dfs(to[i]);
        if(q[to[i]].size()>q[x].size())swap(q[to[i]],q[x]);
        for(p=q[to[i]].begin();p!=q[to[i]].end();p++)
            q[x].insert(*p);
        q[to[i]].clear();
    }
    p=q[x].lower_bound(v[x]);
    if(p!=q[x].end())q[x].erase(p);
    q[x].insert(v[x]);
}

void ins(int x,int y)
{
    nxt[++tot]=lst[x];
    to[tot]=y;
    lst[x]=tot;
}

int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);

    read(n);
    for(int i=1;i<=n;i++)
        read(v[i]),read(x),ins(x,i);

    dfs(1);
    write(q[1].size());

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值