题面

FJOI2018 领导集团问题

给一棵树 \(T(|T|=n)\),每个点有个权值 \(w_i\),从中选出一个子点集 \(P=\{x\in {\rm node}|x\in T\}\),使得 \(\forall u,v\in P,v\in{u{\rm 's\ subtree}}\) 满足 \(w_v\ge w_u\),求 \(|P|_{\max}\)。

数据范围:\(1\le n\le 2\cdot 10^5\),\(0<w_i\le 10^9\)。


蒟蒻解

这里是线段树合并维护 \(\rm dp\) 的做法,\(\Theta(n\log n)\) 但是跑得最慢,觉得这个做法屑的可以在评论区里怒斥 \(\rm George1123\) 逊然后点踩走人,这个低佬一等的蒟蒻需要您的打击。

先对 \(w_i\) 离散化,然后想 \(n^2\) 的 \(\rm dp\):考虑到每个节点要选,它的子树中就只能选 \(\ge\) 它权值的。所以令 \(f(u,x)\) 表示在 \(u\) 的子树中,选的节点权重都 \(\ge x\) 的最大满足题目要求子集大小。

对于一个节点 \(u\),考虑它选不选,可以如下转移 \(dp\):

\[x\in [1,n],f(u,x)\max=\sum_{v\in{u{\rm 's\ subtree}}}f(v,x)
\]
\[x\in [1,w_u],f(u,x)\max=\sum_{v\in{u{\rm 's\ subtree}}}f(v,w_u)
\]

考虑到要区间转移,可以线段树维护;考虑到要子树合并转移,可以线段树合并。

写线段树合并的时候要维护三个操作:权值求和 \(\rm merge\)、区间与 \(x\) 取 \(\max\)、单点求值。

然后就可以做了,但是因为线段树合并上区间修改很困难,所以您旁边的巨佬 zhoukangyang 会怒斥着告诉您一个更好的做法:差分。

很明显 \(\forall x\in[1,n-1],u\in T,f(u,x)\ge f(u,x+1)\),所以令 \(g(u,x)=f(u,x)-f(u,x+1)\),然后维护。

三个操作对应的新做法为:继续权值求和 \(\rm merge\)、线段树上二分找分界点然后差分单点修改、区间求和。

然后就很好写了,线段树上二分找分界点是 \(\Theta(\log n)\) 的,具体看代码。


代码

从 \(0\) 开始的下标证明蒟蒻是个 \(\rm crab\)。

#include <bits/stdc++.h>
using namespace std; //Start
typedef long long ll;
typedef double db;
#define mp(a,b) make_pair((a),(b))
#define x first
#define y second
#define be(a) (a).begin()
#define en(a) (a).end()
#define sz(a) int((a).size())
#define pb(a) push_back(a)
#define R(i,a,b) for(int i=(a),I=(b);i<I;i++)
#define L(i,a,b) for(int i=(b)-1,I=(a)-1;i>I;i--)
const int iinf=0x3f3f3f3f;
const ll linf=0x3f3f3f3f3f3f3f3f; //Data
const int N=2e5;
int n,m,a[N],d[N];
vector<int> e[N];
void adde(int u,int v){e[u].pb(v),e[v].pb(u);} //SegmentTree
const int T=2e7;
#define mid ((l+r)>>1)
int tn,ls[T],rs[T],mx[T];
int newn(){ls[tn]=rs[tn]=-1;return tn++;}
void pushup(int k){
// mx[k]=mx[ls[k]]+mx[rs[k]]; 这里要考虑到空节点
mx[k]=0;
if(~ls[k]) mx[k]+=mx[ls[k]];
if(~rs[k]) mx[k]+=mx[rs[k]];
}
void add(int&k,int x,int v,int l=0,int r=m){
if(r<=x||x+1<=l) return; if(!~k) k=newn();
if(r-l==1) return mx[k]+=v,void();
add(ls[k],x,v,l,mid),add(rs[k],x,v,mid,r),pushup(k);
}
int find(int k,int v,int l=0,int r=m){
if(!~k) return -1; if(v>mx[k]) return -1; if(r-l==1) return l;
int t; if(~(t=find(rs[k],v,mid,r))) return t;
else return find(ls[k],v-(~rs[k]?mx[rs[k]]:0),l,mid); //这里虽然递归了两条路径,但可以证明至少有一条是 O(1) 的
}
int sum(int k,int x,int l=0,int r=m){
if(r<=x) return 0; if(!~k) return 0; if(x<=l) return mx[k];
return sum(ls[k],x,l,mid)+sum(rs[k],x,mid,r);
}
int merge(int k,int p,int l=0,int r=m){
if(!~k||!~p) return ~k?k:p; if(r-l==1) return mx[k]+=mx[p],k;
ls[k]=merge(ls[k],ls[p],l,mid),rs[k]=merge(rs[k],rs[p],mid,r);
return pushup(k),k;
} //Tree
int rt[N];
void dfs(int u=0,int fa=-1){
for(int v:e[u])if(v!=fa)
dfs(v,u),rt[u]=merge(rt[u],rt[v]);
int t=sum(rt[u],a[u]),p=find(rt[u],t+1); //批量转移 dp
add(rt[u],a[u],1),add(rt[u],p,-1);
} //Main
int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
R(i,0,n) cin>>a[i],d[i]=--a[i];
sort(d,d+n),m=unique(d,d+n)-d;
R(i,0,n) a[i]=lower_bound(d,d+m,a[i])-d; // 是 d+m 不是 d+n,lower_bound 玄学函数要写准
R(i,1,n){
int fa; cin>>fa,--fa;
adde(fa,i);
}
fill(rt,rt+n,-1),dfs();
cout<<sum(rt[0],0)<<'\n';
return 0;
}

祝大家学习愉快!

题解-FJOI2018 领导集团问题的更多相关文章

  1. 「题解报告」P4577 [FJOI2018]领导集团问题

    题解 P4577 [FJOI2018]领导集团问题 题解区好像没有线段树上又套了二分的做法,于是就有了这片题解. 题目传送门 怀着必 WA 的决心交了两发,一不小心就过了. 题意 求一个树上最长不下降 ...

  2. 【BZOJ5469】[FJOI2018]领导集团问题(动态规划,线段树合并)

    [BZOJ5469][FJOI2018]领导集团问题(动态规划,线段树合并) 题面 BZOJ 洛谷 题解 题目就是让你在树上找一个最大的点集,使得两个点如果存在祖先关系,那么就要满足祖先的权值要小于等 ...

  3. [FJOI2018]领导集团问题

    [FJOI2018]领导集团问题 dp[i][j],i为根子树,最上面的值是j,选择的最大值 观察dp方程 1.整体Dp已经可以做了. 2.考虑优美一些的做法: dp[i]如果对j取后缀最大值,显然是 ...

  4. [FJOI2018]领导集团问题 mulitset合并

    P4577 [FJOI2018]领导集团问题 链接 luogu bzoj 他是个重题 bzoj4919: [Lydsy1706月赛]大根堆 代码改改就过了 思路 求树上的lis,要好好读题目的!!! ...

  5. P4577 [FJOI2018]领导集团问题

    P4577 [FJOI2018]领导集团问题 我们对整棵树进行dfs遍历,并用一个multiset维护对于每个点,它的子树可取的最大点集. 我们遍历到点$u$时: 不选点$u$,显然答案就为它的所有子 ...

  6. 5469: [FJOI2018]领导集团问题

    5469: [FJOI2018]领导集团问题 链接 题意: 要求在一棵树内选一个子集,满足子集内的任意两个点u,v,如果u是v的祖先,那么u的权值小于等于v. 分析: dp[u][i]表示在u的子树内 ...

  7. 洛谷4577 & LOJ2521:[FJOI2018]领导集团问题——题解

    https://www.luogu.org/problemnew/show/P4577 https://loj.ac/problem/2521 参考:https://www.luogu.org/blo ...

  8. bzoj5469 [FJOI2018]领导集团问题

    题目描述: bz luogu 题解: 相当于树上$LIS$问题. 考虑一维情况下的贪心,我们可以用multiset启发式合并搞. 代码: #include<set> #include< ...

  9. 洛谷P4577 [FJOI2018]领导集团问题(dp 线段树合并)

    题意 题目链接 Sol 首先不难想到一个dp,设\(f[i][j]\)表示\(i\)的子树内选择的最小值至少为\(j\)的最大个数 转移的时候维护一个后缀\(mx\)然后直接加 因为后缀max是单调不 ...

随机推荐

  1. LOJ #2005. 「SDOI2017」相关分析 线段树维护回归直线方程

    题目描述 \(Frank\) 对天文学非常感兴趣,他经常用望远镜看星星,同时记录下它们的信息,比如亮度.颜色等等,进而估算出星星的距离,半径等等. \(Frank\) 不仅喜欢观测,还喜欢分析观测到的 ...

  2. 【涂鸦物联网足迹】涂鸦云平台消息服务—顺带Pulsar简单介绍

    前序系列文章>>> [涂鸦物联网足迹]涂鸦云平台标准指令集 开放消息平台主要通过 Pulsar 主动推送各种事件数据给外部合作伙伴,以满足合作伙伴对消息实时性和消息持久化的要求. 一 ...

  3. ceph扩展bluestore的db分区

    前言 在ceph 14版本里面才加入了bluefs-bdev-migrate,分区迁移相关的命令,那么在12版本里面其实也是可以扩展分区的 测试的版本 [root@lab102 ceph-0]# ce ...

  4. 01、MyBatis HelloWorld

    1. MyBatis简介 1)MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架 2)MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集 3)MyB ...

  5. Java编发编程 - 线程池的认识(二)

    核心线程池的内部实现 依然参考 JDK 对线程池的支持,各个接口.相关类之间的关系:  (1)对于Executors中几个创建线程池方法底层实现: // 创建固定线程数量的线程池 public sta ...

  6. 金九银十想去跳槽面试?那这份Java面经你真得看看了,写的非常详细!

    前言 前两天在和朋友吃饭的时候聊到时间这个东西是真的过的好坏啊,金三银四仿佛还在昨天.一眨眼金九银十又快到了,对程序员来说这两个是一年最合适的跳槽涨薪环节了,今年的你已经做好准备了吗?不妨看看这篇文章 ...

  7. 在IDM上设置防止过度抓取网站信息

    在使用Internet Download Manager(IDM)下载器时,有时会发现IDM自带的抓取功能过于强大,以至于有时会抓取一些无效的链接.那么,该如何避免IDM的过度抓取呢? 图1:IDM的 ...

  8. FL Studio中echo的延迟作用

    今天来一起研究FL Studio的Echo Delay的作用,Echo Delay可以从MIDI输入创建回声,并允许我们通过音量,声像,切除和共振,音高和时间来操纵延迟. 图1:Echo Dealy ...

  9. python应用(2):写个python程序给自己用

    用python写一个程序,然后在命令行上执行,看不到界面(UI),这种程序很常见了,叫命令行程序.然而很多人,特别是不懂程序的人,更需要看到的是一个有界面的,能通过鼠标操作的程序,毕竟已经迈进&quo ...

  10. 这份SpringMVC执行原理笔记,建议做java开发的好好看看,总结的很详细!

    什么是SpringMVC? Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面.Spring 框架提供的web模块,包含了开发Web 应用程 ...