2020年论文题,这里给出了一个$o(n\log^{2}n+m\log^{3}n)$的做法,例题3即为原题

1.例题1


题面

给定$n$个一次函数$f_{i}(x)$,$m$次查询$F(x)=\max_{i=1}^{n}f_{i}(x)$,强制在线

$1\le n,m\le 3\times 10^{5}$,$\left|[x^{j}]f_{i}(x)\right|,\left|x\right|\le 10^{9}$


DS序列

(以下序列被称为Davenport-Schinzel序列,简称DS序列)

定义序列$\{\sigma_{1},\sigma_{2},...,\sigma_{m}\}$是$DS(n,s)$序列,当且仅当满足以下条件:

1.$\forall 1\le i\le m,\sigma_{i}$是不超过$n$的正整数

2.$\{\sigma_{i}\}$中相邻两数不同(即$\forall 1\le i<m,\sigma_{i}\ne \sigma_{i+1}$)

3.$\{\sigma_{i}\}$不存在长度大于$s+1$的子序列,使得其是由两个不同的元素交替构成

引理1:记$\lambda_{s}(n)$为最长的$DS(n,s)$​序列长度,则有
$$
\lambda_{s}(n)=\begin{cases}n&(s=1)\\2n-1&(s=2)\\2n\alpha(n)+O(n)&(s=3)\\\Theta(n2^{\alpha(n)})&(s=4)\\\Theta(n\alpha(n)2^{\alpha(n)})&(s=5)\\n2^{\frac{\alpha(n)^{t}}{t!}+O(\alpha(n)^{t-1})}&(s\ge 6)\end{cases}
$$
(其中$t=\lfloor\frac{s-2}{2}\rfloor$)


题解

称两个函数$f(x)$和$g(x)$是$s$阶交替的,当且仅当$P(x)=[f(x)\le g(x)]$是不超过$s+1$段的分段常函数(显然每一段均为0或1,也即大小关系至多变化$s$次)

进一步的,如果可以$o(s)$求出$P(x)$的分段情况,则称两者是可线性合并的

注意到一次函数两两一阶交替,那么记$G(x)=\min_{1\le i\le n,F(x)=f_{i}(x)}i$,显然$G(x)$是一个分段常函数,将其中的常数取出构成一个序列,不难得到这是一个$DS(n,1)$序列

进而分治求出$G(x)$,又因为其可线性合并,预处理复杂度即$o(\lambda_{1}(n)\log n)=o(n\log n)$

每一次查询二分即可,查询复杂度即$o(m\log n)$

最终,总复杂度为$o(n\log n+m\log n)$,可以通过


拓展

在原问题的基础上,考虑函数在线加入如何处理:

维护一个类似于动态开点线段树的结构,但在区间内线段还不满时不进行合并,显然其均摊复杂度即与分治的复杂度相同,仍为$o(n\log n)$

考虑查询,即需要对$o(\log n)$个部分分别二分,这样的复杂度会变为$o(m\log^{2}n)$

考虑分散层叠算法,参考[luogu6466](注意这里要从高到低合并),那么复杂度与修改复杂度相同,均摊后同样是$o(n\log n)$的,同时查询复杂度降为$o(m\log n)$

最终,总复杂度为$o(n\log n+m\log n)$,可以通过


应用

(对于拓展问题)当函数有界限时(即线段),可以用李超线段树$o(n\log^{2}n+m\log n)$实现

而借助此做法,将线段外的部分用$y=-\infty$补全,注意到这些函数两两3阶交替且可线性合并,套用上述算法时间复杂度即$o(n\alpha(n)\log n+m\log n)$,略优于李超线段树

2.例题2


题面

给定$n$个一次函数$f_{i}(x)$,$m$次操作,支持:

1.修改$f_{i}(x)$(仍是一次函数)

2.查询$F(x)=\max_{i=1}^{n}f_{i}(x)$,保证$x$单调不下降

强制在线

$1\le n,m\le 10^{5}$,$\left|[x^{j}]f_{i}(x)\right|,\left|x\right|\le 10^{9}$

(以下为了描述复杂度,均用$m_{i}$代指第$i$种操作的次数)


题解

维护一棵线段树,线段树上每一个节点维护在区间内(对于当前的$x$)取到最大值的函数

当$x$增大时,一个节点被修改的必要条件为满足以下两者之一:1.其子树内有节点被修改;2.其两个儿子维护函数的大小关系发生了变化

由此,维护子树中每一个节点两个儿子下一次大小关系发生变化(相对于当前的$x$)的位置最小值,递归时若更改的$x$没有达到该值即可直接退出

而大小关系发生变化实际上与之前的一阶交替是相似的,因此每一个节点被直接修改(即满足第2个条件)的次数为区间长度,进而会使得其到根路径上$o(\log n)$个节点都被访问

考虑每一个节点的贡献,不难发现即为$o(\log^{2}n)$,最终总均摊复杂度为$o(n\log^{2}n)$

修改以通常线段树的形式实现即可,实际复杂度为$o(\log n)$

而对于均摊的复杂度,可以看作在该叶子上额外增加一个函数,同样贡献为$o(\log^{2}n)$

最终,总复杂度为$o(n\log^{2}n+m_{1}\log^{2}n+m_{2})$,可以通过

3.例题3


题面

给定一棵$n$个点的树,每一个点有点权$a_{i}$和$b_{i}$

记$R(v)$为$v$所有祖先(包括$v$)构成的集合,则$w_{v}=\left|\sum_{u\in R(v)}a_{u}\right|\left|\sum_{u\in R(v)}b_{u}\right|$

$m$次操作,支持:

1.给定$u$和$x$,将$a_{u}$增加$x$

2.给定$u$,查询$u$子树中(包括$u$)所有节点$w_{v}$的最大值

$1\le n\le 2\times 10^{5}$,$1\le m\le 10^{5}$,$1\le u\le n$,$0\le \left|a_{i}\right|,\left|b_{i}\right|,x\le 5\times 10^{3}$


题解

注意到$w_{v}=\max(\sum_{u\in R(v)}b_{u}\sum_{u\in R(v)}a_{u},-\sum_{u\in R(v)}b_{u}\sum_{u\in R(v)})$,由于最终也是要取$\max$,不妨仅考虑前一项(两项处理方式类似,分别求出后再取$\max$即可)

记$k_{v}=\sum_{u\in R(v)}b_{u}$,$w_{v}$的初值为$k_{v}\sum_{u\in R(v)}a_{u}$,建立dfs序后即需要支持:

1.给定$l,r$和$x$(其中$x\ge 0$),令$\forall l\le i\le r,w_{i}=k_{i}x+w_{i}$

2.给定$l$和$r$,查询$\max_{l\le v\le r}w_{v}$

第一个操作似乎比较复杂,但实际上可以看作例题2中$x$区间增加(原来是全局增加),那么即可类似地模仿例题2(当然实现上区别还是较大的),具体做法如下——

每一个位置上的函数为$k_{i}x+w_{i}$(注意这里$w_{i}$是修改后的,可以理解为$x$是变化量),线段树上每一个节点上维护在区间内$x=0$时取到最大值的函数(也即$w_{i}$最大值,若$w_{i}$相同则取$k_{i}$最大的)

再维护子树中每一个节点两个儿子下一次大小关系发生变化的位置最小值,同样如果增加的$x$没有达到该值,直接打懒标记即可(注意修改时也要将该值减小),否则继续递归

这个问题的复杂度并不能简单的描述,需要使用势能分析——

注:这里需要用到一次函数的性质,而不再仅仅是一阶交替和可线性合并

定义一个节点"不平衡"当且仅当其两个儿子中维护函数中的较大者斜率严格小于另一者,再定义其势能为所有不平衡节点的深度平方和

势能的范围为$[0,n\log^{2}n]$,也即初始和结束的势能差至多为$o(n\log^{2}n)$

对于修改,提取出所有被访问过的节点(不包括最后打标记的节点)得到一棵新树

对于这棵新树,去掉必然被访问的节点(即不被修改区间包含的区间),注意到这类点仅有$o(\log n)$个,也即最多会使势能增加$o(\log^{3}n)$

对于剩下的新树,考虑其中势能的变化:

对于其中的叶子,其必然从不平衡变为平衡(根据结束条件显然)

另一方面,注意到这些区间都被完全覆盖,因此每一个节点上维护函数的斜率和值均单调不下降

由此,考虑从平衡变为不平衡的点,对于其未成为最大值的儿子,其所维护的函数一定发生变化,这个变化即对应于某一个叶子,将其对势能的影响算到该叶子上

同时,由于其未成为最大值,那么每一个叶子至多被"算"一次

由于以$d^{2}$为势能,即使减去$(d-1)^{2}$后其仍会使得势能减去$o(d)$,即与访问的复杂度抵消

综上,(单次)修改均摊复杂度为$o(\log^{3}n)$

另外,显然(单次)查询均摊复杂度为$o(\log n)$

最终,总复杂度为$o(n\log n+m_{1}\log ^{2}n+m_{2}\log n)$,可以通过

  1 #include<bits/stdc++.h>
2 using namespace std;
3 #define N 200005
4 #define ll long long
5 #define L (k<<1)
6 #define R (L+1)
7 #define mid (l+r>>1)
8 vector<int>v[N];
9 int n,m,p,x,y,a[N],b[N],dfn[N],sz[N],tag[N<<2];
10 struct Seg{
11 int tag[N<<2],mn[N<<2];
12 struct Line{
13 int k;
14 ll b;
15 }f[N<<2];
16 double get_point(Line x,Line y){
17 if (x.k==y.k)return 0x3f3f3f3f;
18 return -1.0*(x.b-y.b)/(x.k-y.k);
19 }
20 void upd(int k,int x){
21 tag[k]+=x,mn[k]-=x;
22 f[k].b+=(ll)x*f[k].k;
23 }
24 void up(int k){
25 double s=get_point(f[L],f[R]);
26 if (s<=0)s=0x3f3f3f3f;
27 s=min(s,(double)0x3f3f3f3f);
28 mn[k]=min(min(mn[L],mn[R]),(int)floor(s));
29 if ((f[L].b>f[R].b)||(f[L].b==f[R].b)&&(f[L].k>f[R].k))f[k]=f[L];
30 else f[k]=f[R];
31 }
32 void down(int k){
33 upd(L,tag[k]);
34 upd(R,tag[k]);
35 tag[k]=0;
36 }
37 void init(int k,int l,int r,int x,Line y){
38 if (l==r){
39 f[k]=y;
40 return;
41 }
42 if (x<=mid)init(L,l,mid,x,y);
43 else init(R,mid+1,r,x,y);
44 }
45 void build(int k,int l,int r){
46 tag[k]=0;
47 if (l==r){
48 mn[k]=0x3f3f3f3f;
49 return;
50 }
51 build(L,l,mid);
52 build(R,mid+1,r);
53 up(k);
54 }
55 void update(int k,int l,int r,int x,int y,int z){
56 if ((l>y)||(x>r))return;
57 if ((x<=l)&&(r<=y)&&(mn[k]>=z)){
58 upd(k,z);
59 return;
60 }
61 down(k);
62 update(L,l,mid,x,y,z);
63 update(R,mid+1,r,x,y,z);
64 up(k);
65 }
66 ll query(int k,int l,int r,int x,int y){
67 if ((l>y)||(x>r))return -1e18;
68 if ((x<=l)&&(r<=y))return f[k].b;
69 down(k);
70 return max(query(L,l,mid,x,y),query(R,mid+1,r,x,y));
71 }
72 }T0,T1;
73 void dfs(int k,int sa,int sb){
74 dfn[k]=++dfn[0],sz[k]=1;
75 sa+=a[k],sb+=b[k];
76 T0.init(1,1,n,dfn[k],Seg::Line{sb,(ll)sa*sb});
77 T1.init(1,1,n,dfn[k],Seg::Line{-sb,-(ll)sa*sb});
78 for(int i=0;i<v[k].size();i++){
79 dfs(v[k][i],sa,sb);
80 sz[k]+=sz[v[k][i]];
81 }
82 }
83 int main(){
84 scanf("%d%d",&n,&m);
85 for(int i=2;i<=n;i++){
86 scanf("%d",&x);
87 v[x].push_back(i);
88 }
89 for(int i=1;i<=n;i++)scanf("%d",&a[i]);
90 for(int i=1;i<=n;i++)scanf("%d",&b[i]);
91 dfs(1,0,0);
92 T0.build(1,1,n),T1.build(1,1,n);
93 for(int i=1;i<=m;i++){
94 scanf("%d%d",&p,&x);
95 if (p==1){
96 scanf("%d",&y);
97 T0.update(1,1,n,dfn[x],dfn[x]+sz[x]-1,y);
98 T1.update(1,1,n,dfn[x],dfn[x]+sz[x]-1,y);
99 }
100 if (p==2)printf("%lld\n",max(T0.query(1,1,n,dfn[x],dfn[x]+sz[x]-1),T1.query(1,1,n,dfn[x],dfn[x]+sz[x]-1)));
101 }
102 return 0;
103 }

[cf1178G]The Awesomest Vertex的更多相关文章

  1. Codeforces 1178G. The Awesomest Vertex

    传送门 首先通过dfs序把子树操作转化为区间操作,求最大值可以用斜率优化. 然后分个块,对每个块维护个凸包.修改时中间的打个标记,边角暴力重构:询问时中间的用斜率优化的方法求,边角的暴力求. 由于此题 ...

  2. CSharpGL(38)带初始数据创建Vertex Buffer Object的情形汇总

    CSharpGL(38)带初始数据创建Vertex Buffer Object的情形汇总 开始 总的来说,OpenGL应用开发者会遇到为如下三种数据创建Vertex Buffer Object的情形: ...

  3. OpenGL中glVertex、显示列表(glCallList)、顶点数组(Vertex array)、VBO及VAO区别

    OpenGL中glVertex.显示列表(glCallList).顶点数组(Vertex array).VBO及VAO区别 1.glVertex 最原始的设置顶点方法,在glBegin和glEnd之间 ...

  4. 集合覆盖 顶点覆盖: set cover和vertex cover

    这里将讲解一下npc问题中set cover和vertex cover分别是什么. set cover: 问题定义: 实例:现在有一个集合A,其中包含了m个元素(注意,集合是无序的,并且包含的元素也是 ...

  5. 顶点着色器详解 (Vertex Shaders)

    学习了顶点处理,你就知道固定功能流水线怎么将顶点从模型空间坐标系统转化到屏幕空间坐标系统.虽然固定功能流水线也可以通过设置渲染状态和参数来改变最终输出的结果,但是它的整体功能还是受限.当我们想实现一个 ...

  6. Vertex and Fragment Shader

    Semantics语义词: 定义:GPU工作时,数据通常暂存在寄存器,那么在Cg中,语义词就指定了输入/输出数据和图形硬件寄存器之间的映射关系. 原理:根据输入语义,图形处理器从某个寄存器取数据:然后 ...

  7. VBO, VAO, Generic Vertex Attribute

    VBO - 用于存储顶点数据的Buffer Object. VAO - 用于组织VBO的对象. Generic Vertex Attribute - 通用顶点属性. For example, the ...

  8. 实施vertex compression所遇到的各种问题和解决办法

    关于顶点压缩,好处是可以减少带宽,一定程度提高加载速度,可以提高约5-10%的fps,特别是mobile上,简单描述就是: 压缩之前(32字节) position float3 12normal fl ...

  9. UnityShader之顶点片段着色器Vertex and Fragment Shader【Shader资料】

    顶点片段着色器 V&F Shader:英文全称Vertex and Fragment Shader,最强大的Shader类型,也是我们在使用ShaderLab中的重点部分,属于可编程管线,使用 ...

随机推荐

  1. mysql8.0.20安装教程,mysql下载安装教程8.0.20

    mysql8.0.20下载安装教程  mysql8.0.20安装教程 mysql安装包+mysql学习视频+mysql面试指南视频教程 下载地址: 链接:https://pan.baidu.com/s ...

  2. 前端开发3年了,竟然不知道什么是 Vue 脚手架?(下)

    上一篇文章<前端开发3年了,竟然不知道什么是 Vue 脚手架?(上)>介绍了什么是脚手架,以及Vue-cli 2.x如何创建项目,创建的项目结构.这篇文章介绍 Vue-cli 3.x 如何 ...

  3. jenkins的安装、配置使用

    1.jenkins的使用 (1).需要先下载安装JDK 配置jdk的环境 变量JAVA_HOME的值是 jdk 的安装位置, 然后下载安装tomcat 安装好了之后,打开tomcat下的bin文件夹, ...

  4. SharkCTF2021 bbpop题记

    一道挺好的web. 做完这一题,感觉php序列化(甚至魔术方法)之类的有点开始玩明白了. 题面很长: 预备知识: PHP类的方法中,有一部分以下划线开头的"魔术方法".不同于普通方 ...

  5. 分割迭代器Spliterator源码文档翻译

    前言 身体是革命的本钱,不舒服了2周,现在好点了. 学习JDK8的Stream,Spliterator这个分割迭代器是必须要重视的. Notes:下方蓝色文字是自己的翻译(如果有问题请指正).黑色文字 ...

  6. 理解ASP.NET Core - 路由(Routing)

    注:本文隶属于<理解ASP.NET Core>系列文章,请查看置顶博客或点击此处查看全文目录 Routing Routing(路由):更准确的应该叫做Endpoint Routing,负责 ...

  7. 第6次 Beta Scrum Meeting

    本次会议为Beta阶段第6次Scrum Meeting会议 会议概要 会议时间:2021年6月8日 会议地点:「腾讯会议」线上进行 会议时长:15min 会议内容简介:对完成工作进行阶段性汇报:对下一 ...

  8. [技术博客] 通过ItemTouchHelper实现侧滑删除功能

    通过ItemTouchHelper实现侧滑删除功能 一.效果 二.具体实现 demo中演示的这种左滑删除的效果在手机APP中比较常用,安卓也为我们提供了专门的辅助类ItemTouchHelper来帮助 ...

  9. [技术博客] 软工-Ruby on Rails 后端开发总结分享

    [技术博客] 软工-Ruby on Rails 后端开发总结分享 在这次软件编写中,我们的后端使用了Ruby on Rails (RoR)框架. Rails框架是用Ruby编写的.这意味着当我们为Ru ...

  10. Python课程笔记(八)

    一些简单的文件操作,学过linux的话理解感觉不会很难.课程代码 一.OS 目录方法 这个模块提供了一种方便的使用操作系统函数的方法 函数 说明 os.mkdir("path") ...