题意:长度为nnn的序列,qqq次询问,每次给定一个区间,钦定区间中的一个位置xxx,使得区间所有点 与xxx之间的最大值(含端点) 之和 最小,输出最小值。

n,q≤7.5×105n,q\leq7.5\times10^5n,q≤7.5×105

神仙题,不愧是IOI

首先有一个O(n2)O(n^2)O(n2)的 dp

f(l,r)=min⁡{f(l,k−1)+(r−k+1)hk,(k−l+1)hk+f(k+1,r)}f(l,r)=\min\{f(l,k-1)+(r-k+1)h_k,(k-l+1)h_k+f(k+1,r)\}f(l,r)=min{f(l,k−1)+(r−k+1)hk​,(k−l+1)hk​+f(k+1,r)}

其中kkk是l,rl,rl,r中的最大值的位置(如有多个随便选一个),即讨论钦定的点在最大值的左侧或右侧,然后另一侧的点贡献都是最大值

我觉得我考场上能想到这步就不错了

这个 dp 转移已经O(1)O(1)O(1)了,也不好压成一维,所以要么用可持久化之类的东西强行压状态,要么就不记录无用的状态

直接想的话两条路都不好走,但注意到这个过程实际上是最值分治,自然地想到笛卡尔树

哪里自然了啊kora

具体地讲,建出序列的笛卡尔树,然后在树上做上面dp,每个点只记录它代表的区间的dp值,这样就可以O(n)O(n)O(n)处理出来了。

然而就算处理出来了你仍然无法快速计算答案,因为询问区间可能会被拆成很多小段,你需要像平衡树一样沿着树递归下去。而笛卡尔树高是O(n)O(n)O(n)的,仍然可以被卡成狗。

不过思路感觉很对,考虑怎么优化

观察一下这个dp方程式

f(l,r)=min⁡{f(l,k−1)+(r−k+1)hk,f(k+1,r)+(k−l+1)hk}f(l,r)=\min\{f(l,k-1)+(r-k+1)h_k,f(k+1,r)+(k-l+1)h_k\}f(l,r)=min{f(l,k−1)+(r−k+1)hk​,f(k+1,r)+(k−l+1)hk​}

套到树上:当前子树根结点是kkk,为了计算f(l,k−1)f(l,k-1)f(l,k−1)和f(k+1,r)f(k+1,r)f(k+1,r),我们需要继续往左右子树递归计算,我们这样子是不行的

但这个f(l,k−1)f(l,k-1)f(l,k−1)和f(k+1,r)f(k+1,r)f(k+1,r)比较特殊:它们都有一个端点是固定的!

为了叙述方便,下面只讨论f(k+1,r)f(k+1,r)f(k+1,r),左边的f(l,k−1)f(l,k-1)f(l,k−1)是同理的

我们要是知道右子树的区间的所有前缀的dp信息就好了

看上去很扯,但实际上是可行的!

假设我们分别知道kkk的左右子树的前缀信息,也就是知道f(l,l...k−1)f(l,l...k-1)f(l,l...k−1)和f(k+1,k+1...r)f(k+1,k+1...r)f(k+1,k+1...r),现在考虑怎么合并f(l,l...r)f(l,l...r)f(l,l...r)

显然左边是不用管的

对于右边,我们再把这个方程式搬出来。为了看着顺眼,我把rrr换成了iii

f(l,i)=min⁡{f(l,k−1)+(i−k+1)hk,f(k+1,i)+(k−l+1)hk}f(l,i)=\min\{f(l,k-1)+(i-k+1)h_k,f(k+1,i)+(k-l+1)h_k\}f(l,i)=min{f(l,k−1)+(i−k+1)hk​,f(k+1,i)+(k−l+1)hk​}

注意这个iii是在[k+1,r][k+1,r][k+1,r]内的,冷静分析一下,发现这个东西就是在原来的基础上整体加一个数,然后和一个一次函数取min⁡\minmin,因为是个区间,所以可以用线段树维护!

而这个方程是可以找到一个分界点,使得分界点左边取左边的值,右边取右边的值。原因是iii每增加111,左边的值固定增加hkh_khk​,而右边增加f(k+1,i+1)−f(k+1,i)f(k+1,i+1)-f(k+1,i)f(k+1,i+1)−f(k+1,i),区间往右扩张一位增加的代价总不可能大于区间最大值吧……所以左边迟早会超过右边,而且不会被反超。线段树上二分即可。

什么?线段树开不下?

但仔细想想会发现不同的位置是不会冲突的,即每个点xxx维护它当前处理的左端点到xxx的dp值,所以开一个线段树就可以了。

线段树合并应该也可以

f(l,k−1)f(l,k-1)f(l,k−1)的话再开一棵线段树,左右倒过来就可以了

然后把询问离线,每组询问挂在区间最大值的点上面,建笛卡尔树的时候顺便处理一下就可以了。

复杂度O(nlog⁡n)O(n\log n)O(nlogn)

#include <iostream>
#include <cstring>
#include <cctype>
#include <cstdio>
#include <vector>
#define MAXN 750005
using namespace std;
inline int read()
{int ans=0;char c=getchar();while (!isdigit(c)) c=getchar();while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();return ans;
}
typedef long long ll;
int n;
struct SegmentTree
{#define lc p<<1#define rc p<<1|1struct node{int l,r,tag;ll k,b,lv,rv;}t[MAXN<<2];void build(int p,int l,int r){t[p].l=l,t[p].r=r;if (l==r) return;int mid=(l+r)>>1;build(lc,l,mid),build(rc,mid+1,r);}inline void pushcov(int p,ll k,ll b){t[p].k=k,t[p].b=b;t[p].lv=k*t[p].l+b,t[p].rv=k*t[p].r+b;t[p].tag=1;}inline void pushadd(int p,ll k,ll b){t[p].k+=k,t[p].b+=b;t[p].lv+=k*t[p].l+b,t[p].rv+=k*t[p].r+b;if (!t[p].tag) t[p].tag=2;        }inline void pushdown(int p){if (t[p].tag){if (t[p].tag==1) pushcov(lc,t[p].k,t[p].b),pushcov(rc,t[p].k,t[p].b);if (t[p].tag==2) pushadd(lc,t[p].k,t[p].b),pushadd(rc,t[p].k,t[p].b);t[p].tag=t[p].k=t[p].b=0;}}inline void update(int p){t[p].lv=t[lc].lv,t[p].rv=t[rc].rv;} ll query(int p,int k){if (t[p].l==t[p].r) return t[p].lv;pushdown(p);if (k<=t[lc].r) return query(lc,k);return query(rc,k);}void search(int p,int l,int r,ll k1,ll b1,ll b2){if (l<=t[p].l&&t[p].r<=r){if (k1*t[p].l+b1<=t[p].lv+b2&&k1*t[p].r+b1<=t[p].rv+b2) return pushcov(p,k1,b1);if (t[p].lv+b2<=k1*t[p].l+b1&&t[p].rv+b2<=k1*t[p].r+b1) return pushadd(p,0,b2);}if (r<t[p].l||t[p].r<l) return;pushdown(p);search(lc,l,r,k1,b1,b2),search(rc,l,r,k1,b1,b2);update(p);}
}lt,rt;
int h[MAXN];
inline int Max(const int& x,const int& y){return h[x]>h[y]? x:y;}
int st[20][MAXN],LOG[MAXN];
inline int rmq(int l,int r)
{int t=LOG[r-l+1];return Max(st[t][l],st[t][r-(1<<t)+1]);
}
int ql[MAXN],qr[MAXN];
ll ans[MAXN];
vector<int> lis[MAXN];
void solve(int l,int r)
{if (l>r) return;int k=rmq(l,r);solve(l,k-1),solve(k+1,r);for (int i=0;i<(int)lis[k].size();i++){int L=ql[lis[k][i]],R=qr[lis[k][i]];ans[lis[k][i]]=h[k]*(R-L+1ll);if (L<k) ans[lis[k][i]]=min(ans[lis[k][i]],rt.query(1,L)+(R-k+1ll)*h[k]);if (k<R) ans[lis[k][i]]=min(ans[lis[k][i]],(k-L+1ll)*h[k]+lt.query(1,R));}ll tl=rt.query(1,l),tr=lt.query(1,r);lt.search(1,k,r,h[k],tl+h[k]*(1ll-k),(k-l+1ll)*h[k]);rt.search(1,l,k,-h[k],tr+h[k]*(k+1ll),(r-k+1ll)*h[k]);
}
int main()
{n=read();int q=read();for (int i=1;i<=n;i++) h[i]=read();lt.build(1,1,n);rt.build(1,1,n);for (int i=1;i<=n;i++) st[0][i]=i;for (int j=1;j<20;j++)for (int i=1;i+(1<<(j-1))<=n;i++)st[j][i]=Max(st[j-1][i],st[j-1][i+(1<<(j-1))]);LOG[0]=-1;for (int i=1;i<=n;i++) LOG[i]=LOG[i>>1]+1;for (int i=1;i<=q;i++) ql[i]=read()+1,qr[i]=read()+1,lis[rmq(ql[i],qr[i])].push_back(i);solve(1,n);for (int i=1;i<=q;i++) printf("%lld\n",ans[i]);return 0;
}

【IOI2018】会议【笛卡尔树】【dp】【线段树】相关推荐

  1. hdu5489 Removed Interval dp+线段树优化

    现在看这题居然直接秒了...去年看的时候还以为神题.. 设以第i项为结尾的lis前缀为f[i],以第j项为结尾的lis后缀为g[i],如果求出f[i]和g[j],然后枚举i,快速找到最大的满足a[j] ...

  2. 【UOJ#388】【UNR#3】配对树(线段树,dsu on tree)

    [UOJ#388][UNR#3]配对树(线段树,dsu on tree) 题面 UOJ 题解 考虑一个固定区间怎么计算答案,把这些点搞下来建树,然后\(dp\),不难发现一个点如果子树内能够匹配的话就 ...

  3. P1047 校门外的树(线段树优化)(校门三部曲)难度⭐⭐

    校门三部曲,总算完结了!完结散花! 难度呈阶梯状,都可以用线段树解决. 第一部 P1047 校门外的树(线段树优化)难度⭐⭐ 第二部 P1276 校门外的树(增强版)(线段树)校门三部曲难度⭐⭐⭐ 第 ...

  4. 【BZOJ-13962865】识别子串字符串识别 后缀自动机/后缀树组 + 线段树

    1396: 识别子串 Time Limit: 10 Sec  Memory Limit: 162 MB Submit: 312  Solved: 193 [Submit][Status][Discus ...

  5. java 区间树_线段树(区间树)之区间染色和4n推导过程

    前言 线段树(区间树)是什么呢?有了二叉树.二分搜索树,线段树又是干什么的呢?最经典的线段树问题:区间染色:正如它的名字而言,主要解决区间的问题 一.线段树说明 1.什么是线段树? 线段树首先是二叉树 ...

  6. 模板:二维线段树(线段树套线段树)

    文章目录 问题 解析 单点修改 询问 完整代码 标记永久化 代码 所谓二维线段树,就是有两个维度的线段树 (逃) 问题 给出一个矩形 要求支持以下操作: 1.询问一个子矩形的最值 2.修改某一个单点的 ...

  7. BZOJ3531-[Sdoi2014]旅行(树剖+线段树动态开点)

    传送门 完了今天才知道原来线段树的动态开点和主席树是不一样的啊 我们先考虑没有宗教信仰的限制,那么就是一个很明显的树剖+线段树,路径查询最大值以及路径和 然后有了宗教信仰的限制该怎么做呢? 先考虑暴力 ...

  8. [BZOJ4372][烁烁的游戏][动态树分治+线段树+LCA]

    [BZOJ4372][烁烁的游戏][动态树分治+线段树+LCA] 题目大意: 给定一颗nn个节点的树,边权均为11,初始每个点权值为00 . 其中操作QQ xx询问x点的点权,操作 MM xx dd ...

  9. 线段树简单入门 (含普通线段树, zkw线段树, 主席树)

    线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ...

  10. Acwing 4339 敌兵布阵 暴力 + 分块 + 线段树 + Zkw线段树 + 树状数组

    来一篇超全题解 数据结构大杂烩 原题连接 题目描述 敌人有 NNN 个工兵营地,编号 1∼N1∼N1∼N. 初始时,第 iii 个营地有 aia_iai​ 个人. 接下来有若干个命令,命令有 444 ...

最新文章

  1. #Ruby# Introspect (2)
  2. 求任意数阶乘最后一位
  3. const成员或者引用成员必须使用构造函数初始化列表的方式
  4. 【项目管理】Scrum内容整理
  5. 一块小饼干(Cookie)的故事-下篇
  6. 现在是不是很多人都不愿意在银行存钱?
  7. linux处置服务Iptables
  8. 圆管当量直径_截面相同时,正方型截面的当量直径大于圆管直径。( )。
  9. oracle在linux上使用裸设备,在linux下构建基于LVM的裸设备数据库
  10. 【编程珠玑】第五章 编程小事
  11. 企业如何高效用云?| 资深运维架构师细说云架构下的运维体系构建
  12. 投资平台服务器状态未知,投资者说20130606:503 service unavailable错误解决教程
  13. java移位运算_Java 移位运算符
  14. 2022淘宝618超级喵运会怎么玩?2022淘宝618喵运会玩法技巧
  15. 无法获得下列许可solidworks standard无效的(不一致的)使用许可号码(-8,544,0) solidworks2020 (亲测有效)
  16. Java Web学习—编程1:数字三角形+三种解题思路+详细规律
  17. 定向思维 C# datediff
  18. ES6新增API - Reflect
  19. PHP生成订单号算法
  20. [SCOI2016]幸运数字

热门文章

  1. java 数组拼接_打印Java数组最优雅的方式是什么?这波操作闪瞎我
  2. 全地球的水也没办法将这个“特殊”的瓶子装满!
  3. 有一个会泰勒级数的八岁表妹是怎样一种体验?
  4. 恐龙的丁丁长什么样?它们是怎么啪啪啪的?这项研究网友看完直呼涨姿势.........
  5. 老是担心数学学不好?是因为你的数学老师不是爱因斯坦!
  6. 专业学习频道,欢迎关注数锐学堂
  7. 这是我见过的最全的训练数据集,没有之一!
  8. python选择题题目_Python接口测试题(持续更新中)
  9. python区域增长算法_区域增长算法
  10. icd11中文版精神障碍pdf_精神与行为障碍类别目录(ICD-11)