「FJOI2018」领导集团问题

题意:给你一颗\(n\)个点的带点权有根树,选择一个点集\(S\),使得点集中所有祖先的点权$\le \(子孙的点权,最大化\)|S|$(出题人语死早...)


  • 一个显然的\(dp\)

    \(dp_{i,j}\)代表子树\(i\)中选择的点集中最小的点权为\(j\)时的最大值。

  • 一个朴素的转移需要维护一个后缀最大值

    设为\(f_{i,j}=\max\limits_{k\ge i} dp_{i,k}\)

  • 一些显然的事实

    • 后缀最大值中仅有最多\(siz_v\)(指子树大小)个权值不同的位置
    • 后缀最大值单调不增

于是我们考虑如何合并两颗子树有限的后缀最大值

在不考虑关键点时,我们只需要对应位置两两加和就可以了,即

\[dp_{i,j}=\sum_vf_{v,j}
\]

下面我们仅考虑关键点

如图所示,蓝色的关键点是子树\(u\)的关键点,黄色的是子树\(v\)的关键点,注意到关键点是左边位置的值与它相等。

考虑启发式合并,但是我们注意到关键点是互相影响的,就是黄影响蓝,蓝影响黄。

其实这样可以做,但是比较麻烦。

注意到一个事实,我们只需要求\(f_{rt,0}\),所以我们考虑维护后缀最大值\(f\)的差分数组。

这样我们合并两个关键点集合,直接对关键点进行相加就可以了,考虑直接用线段树合并维护。

然后我们还需要计算根\(i\)自己的贡献,设\(i\)的权值为\(w_i\)

考虑到根直接的贡献是使\(dp_{i,w_i}=f_{i,w_i}+1\)

然后从这个\(w_i\)往左边走,找到第一个\(f_{i,p}\)比\(f_{i,w_i}\)大\(1\)的位置,然后更新这一段的贡献,就是一个区间加。具体操作就是在\(p\)打个\(-1\),在\(w_i\)打一个\(1\)

形象一点就是把这一段铺平了,就是那\(dp\)再回去贡献\(f\)

找第一个位置就是在差分意义下就是找一个非\(0\)位置,可以在线段树上进行二分操作找到。


Code:

#include <cstdio>
#include <cctype>
#include <vector>
#include <algorithm>
using std::min;
using std::max;
const int SIZE=1<<21;
char ibuf[SIZE],*iS,*iT;
//#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,SIZE,stdin),iS==iT?EOF:*iS++):*iS++)
#define gc() getchar()
template <class T>
void read(T &x)
{
int f=0;x=0;char c=gc();
while(!isdigit(c)) f|=c=='-',c=gc();
while(isdigit(c)) x=x*10+c-'0',c=gc();
if(f) x=-x;
}
const int N=4e5+10;
int n,m,w[N],yuy[N];
int head[N],to[N],Next[N],cnt;
void add(int u,int v)
{
to[++cnt]=v,Next[cnt]=head[u],head[u]=cnt;
}
int root[N],ch[N*40][2],sum[N*40],tot;
#define ls ch[now][0]
#define rs ch[now][1]
void upt(int &now,int l,int r,int p,int d)
{
if(!now) now=++tot;
if(l==r)
{
sum[now]+=d;
return;
}
int mid=l+r>>1;
if(p<=mid) upt(ls,l,mid,p,d);
else upt(rs,mid+1,r,p,d);
sum[now]=sum[ls]+sum[rs];
}
int qry(int now,int l,int r,int p)
{
if(!now||!p) return 0;
if(l==r) return sum[now]?l:0;
int mid=l+r>>1;
if(p<=mid) return qry(ls,l,mid,p);
else
{
int ret=qry(rs,mid+1,r,p);
if(ret) return ret;
else return qry(ls,l,mid,p);
}
}
int Merge(int x,int y,int l,int r)
{
if(!x||!y) return x^y;
if(l==r)
{
sum[x]+=sum[y];
return x;
}
int mid=l+r>>1;
ch[x][0]=Merge(ch[x][0],ch[y][0],l,mid);
ch[x][1]=Merge(ch[x][1],ch[y][1],mid+1,r);
sum[x]=sum[ch[x][0]]+sum[ch[x][1]];
return x;
}
void dfs(int now)
{
for(int v,i=head[now];i;i=Next[i])
{
dfs(v=to[i]);
root[now]=Merge(root[now],root[v],0,m);
}
upt(root[now],1,m,w[now],1);
int pos=qry(root[now],1,m,w[now]-1);
if(pos) upt(root[now],1,m,pos,-1);
}
int main()
{
read(n);
for(int i=1;i<=n;i++) read(w[i]),yuy[i]=w[i];
std::sort(yuy+1,yuy+1+n);
m=std::unique(yuy+1,yuy+1+n)-yuy-1;
for(int i=1;i<=n;i++) w[i]=std::lower_bound(yuy+1,yuy+1+m,w[i])-yuy;
for(int p,i=2;i<=n;i++) read(p),add(p,i);
dfs(1);
printf("%d\n",sum[root[1]]);
return 0;
}

2019.5.21

「FJOI2018」领导集团问题 解题报告的更多相关文章

  1. 【LOJ】 #2521. 「FJOI2018」领导集团问题

    题解 这道题很显然可以想出来一个\(n^2\)的dp,也就是dp[u][i]表示以u为根的子树最大值是i的点集最大是多少(i是离散化后的值) 就是对于每个儿子处理出后缀最大值然后按位相加更新父亲,我们 ...

  2. 洛谷 P4714 「数学」约数个数和 解题报告

    P4714 「数学」约数个数和 题意(假):每个数向自己的约数连边,给出\(n,k(\le 10^{18})\),询问\(n\)的约数形成的图中以\(n\)为起点长为\(k\)的链有多少条(注意每个点 ...

  3. 「NOI2013」树的计数 解题报告

    「NOI2013」树的计数 这什么神题 考虑对bfs重新编号为1,2,3...n,然后重新搞一下dfs序 设dfs序为\(dfn_i\),dfs序第\(i\)位对应的节点为\(pos_i\) 一个暴力 ...

  4. 「NOI2016」优秀的拆分 解题报告

    「NOI2016」优秀的拆分 这不是个SAM题,只是个LCP题目 95分的Hash很简单,枚举每个点为开头和末尾的AA串个数,然后乘一下之类的. 考虑怎么快速求"每个点为开头和末尾的AA串个 ...

  5. 「NOI2016」循环之美 解题报告

    「NOI2016」循环之美 对于小数\(\frac{a}{b}\),如果它在\(k\)进制下被统计,需要满足要求并且不重复. 不重复我们确保这个分数是最简分数即\((a,b)=1\) 满足要求需要满足 ...

  6. 「SP25784」BUBBLESORT - Bubble Sort 解题报告

    SP25784 BUBBLESORT - Bubble Sort 题目描述 One of the simplest sorting algorithms, the Bubble Sort, can b ...

  7. 「SP122」STEVE - Voracious Steve 解题报告

    SP122 STEVE - Voracious Steve 题意翻译 Problem Steve和他的一个朋友在玩游戏,游戏开始前,盒子里有 n个甜甜圈,两个人轮流从盒子里抓甜甜圈,每次至少抓 1个, ...

  8. 「Luogu」[JSOI2007]字符加密 解题报告

    题面 思路: 作为一个后缀数组的初学者,当然首先想到的是后缀数组 把\(s\)这个串首尾相接,扩展为原来的两倍,就能按后缀数组的方法处理 证明: 神仙一眼就看出这是后缀的裸题,我这个蒟蒻想了半天想不出 ...

  9. 「P5004」专心OI - 跳房子 解题报告

    题面 把\(N\)个无色格子排成一行,选若干个格子染成黑色,要求每个黑色格子之间至少间隔\(M\)个格子,求方案数 思路: 矩阵加速 根据题面,这一题似乎可以用递推 设第\(i\)个格子的编号为\(i ...

随机推荐

  1. SparkStreaming获取kafka数据的两种方式:Receiver与Direct

    简介: Spark-Streaming获取kafka数据的两种方式-Receiver与Direct的方式,可以简单理解成: Receiver方式是通过zookeeper来连接kafka队列, Dire ...

  2. PHP opendir() 函数

    打开一个目录,读取它的内容,然后关闭: <?php$dir = "/images/"; // Open a directory, and read its contentsi ...

  3. 移动 web 端屏幕适配 - rem

    前言 最近整理了一下以前学习前端的笔记,发现自己对移动 web 端屏幕适配(rem)这一块并没有真正理解,只是会用.接下来,把自己的一些对移动 web 端屏幕适配(rem)的思考记录下来. rem 介 ...

  4. ROM、PROM、EPROM、EEPROM、Flash ROM分别指什么?

    ROM指的是“只读存储器”,即Read-Only Memory.这是一种线路最简单半导体电路,通过掩模工艺, 一次性制 造,其中的代码与数据将永久保存(除非坏掉),不能进行修改.这玩意一般在大批量生产 ...

  5. float不完整带来的IE7下的不兼容

    这种原因是因为搜索用了float:right;添加报考院校和导入文件没有用float; 解决的方法是:1.给添加报考院校和导入文件分别添加float:left;2.把搜索那部分代码写在添加报考院校和导 ...

  6. POJ3641 Pseudoprime numbers (幂取模板子)

    给你两个数字p,a.如果p是素数,并且ap mod p = a,输出“yes”,否则输出“no”. 很简单的板子题.核心算法是幂取模(算法详见<算法竞赛入门经典>315页). 幂取模板子: ...

  7. docker x509: certificate has expired or is not yet valid

    系统环境:centos 6.5 内核版本:2.6.32-696.1.1.el6.x86_64 程序版本:Docker version 1.7.1, build 786b29d/1.7.1 问题:下载镜 ...

  8. MySQL全面优化,速度飞起来!

    Java技术栈 www.javastack.cn 优秀的Java技术公众号 作者:惨绿少年 https://www.cnblogs.com/clsn/p/8214048.html 在进行MySQL的优 ...

  9. Hooks初探

    一.创建自己的HOOkS,并进行封装 二.创建自己的HOOkS,并进行封装

  10. idea设置忽略svn的文件或目录

    1. 这个地方可以设置忽略的文件和目录,但是这里设置之后,我们在工程里面就看不到了 2. 这里设置提交是后要忽略的文件,比如我忽略的target目录,*.iml 我再1中设置了target目录发现我的 ...