题面

时间限制:1s,空间限制:1024MB

题目描述

手拿咒刃砍金门,众神直呼不是人

椅子玩自定义咒刃,一路打到了第三关的银行,祂想在黑色大桥无伤看守者之前找点刺激的。

加了模组的银行极大。具体地说,这一关有 nnn 个战斗场景按顺序排成一行,依次编号为 1∼n1\sim n1∼n ,椅子一开始在第一个战斗场景,而黑色大桥在第 nnn 个场景之后。

每个场景在开头有个金门,每个金门里有一把咒刃,每个场景都有一只怪(细胞三爹:拳皇,鸡哥,三刀哥)。

第 iii 个场景的咒刃有四个属性:第一刀伤害 bib_ibi​,后摇 kik_iki​ ,加速条件杀敌数 aia_iai​ ,加速范围 did_idi​ 。

由于上一关的咒刃等级太低,所以椅子必须得砍第一个金门拿到第一把咒刃。除此之外,椅子在后面的任意场景开头都可以砍金门,获得新的咒刃,并用那把咒刃直到下一次砍金门或者到达黑色大桥。

刚拿到第 iii 个场景的咒刃时,第一刀会非常刺激,产生 bib_ibi​ 刺激度,但后摇会降低刺激度 kik_iki​,直到开始加速。当拿这把刀杀敌数达到 ai−dia_i-d_iai​−di​ 时,刺激度会呈朝下的抛物线(二次项系数为 111)变化,杀敌数 aia_iai​ 时到达顶点,此后刺激度开始降低。一直到杀敌数 ai+dia_i+d_iai​+di​ 时,由于长时间保持无伤打怪,刺激度随着疫病上来了,此后又呈二次函数上升……

具体地说,如果带上第 lll 个金门的咒刃一直打到第 rrr 场景结束,将会产生 fl(r−l+1)f_l(r-l+1)fl​(r−l+1) 的刺激度。函数 fi(x)f_i(x)fi​(x) 定义如下:
fi(x)={−kix+bi(x≤ai−di)−ki(ai−di)+bi+di2−(ai−x)2(ai−di<x<ai+di)−ki(ai−di)+bi+(x−(ai+di))2(x≥ai+di)f_i(x)=\begin{cases} -k_ix+b_i~~&~(x\leq a_i-d_i)\\ -k_i(a_i-d_i)+b_i+d_i^2-(a_i-x)^2~~&~(a_i-d_i<x<a_i+d_i)\\ -k_i(a_i-d_i)+b_i+(x-(a_i+d_i))^2~~&~(x\geq a_i+d_i) \end{cases} fi​(x)=⎩⎪⎨⎪⎧​−ki​x+bi​  −ki​(ai​−di​)+bi​+di2​−(ai​−x)2  −ki​(ai​−di​)+bi​+(x−(ai​+di​))2  ​ (x≤ai​−di​) (ai​−di​<x<ai​+di​) (x≥ai​+di​)​

形状类似下图:

开了新的金门后,先前的刺激度会停止变化,随后刺激度将加上新的咒刃带来的刺激度。也就是说,把每一把咒刃带来的刺激度累加就是总刺激度。

椅子希望找到一种合适的砍金门方案,使得最终总刺激度最大。但是祂正在直播,所以把这个问题交给了你。

请输出最大总刺激度。

输入格式

第一行一个数字 nnn ,后面 nnn 行,每行四个数字 ki,ai,bi,dik_i,a_i,b_i,d_iki​,ai​,bi​,di​ ,如题意。每个数字之间用单个空格分隔。

nnn

k1a1b1d1k_1~a_1~b_1~d_1k1​ a1​ b1​ d1​

k2a2b2d2k_2~a_2~b_2~d_2k2​ a2​ b2​ d2​

.........

knanbndnk_n~a_n~b_n~d_nkn​ an​ bn​ dn​

输出格式

一行一个数字,表示最大总刺激度。

样例输入

#1

5
1 2 8 1
9 3 8 2
4 3 3 3
4 1 2 1
7 2 1 2

#2

4
1 4 1 4
1 4 2 4
1 4 1 4
1 4 3 4

#3

4
2 1 8 1
5 2 1 2
3 1 1 1
1 2 1 2

样例输出

#1

23

#2

35

#3

19

提示

#1

依次拿第 1,3,51,3,51,3,5 把咒刃,可以获得最大刺激度 8+11+4=238+11+4=238+11+4=23 。

#2

符合数据 6~10 的特殊限制

#3

符合数据 16~20 的特殊限制

数据范围

对于所有数据,1≤ki,bi≤106,1≤di≤ai≤n≤1061\leq k_i,b_i\leq 10^6,1\leq d_i\leq a_i\leq n\leq 10^61≤ki​,bi​≤106,1≤di​≤ai​≤n≤106 .

测试点编号 n≤n\leqn≤ 特殊限制
1~5 5×1035\times10^35×103
6~10 10610^6106 ai=di=na_i=d_i=nai​=di​=n
11~15 10510^5105
16~20 10610^6106 ai+di≥na_i+d_i\geq nai​+di​≥n
21~25 10610^6106

暖心提醒:本题的输入量较大(甚至用 std::scanf() 都会有小概率卡常),请使用较快的读入方式

题解

法 1

通用解法

60pts

观察这个函数的三个部分

  • 直线
  • (二次项系数均为 -1 的)抛物线
  • (二次项系数均为 1 的)抛物线

当然,为了 DP 转移,我们得在常数项上加上左边的 DP 值。

我们发现,这三者都可以单独用李超线段树维护最大值。

李超线段树可以合理维护的函数,只需要满足两个函数最多只有一个交点(或者延伸至无穷的一段区间)。

所以,每次在特定区间添加函数,用李超线段树维护单点最大值,时间复杂度 O(nlog⁡2n)O(n\log^2n)O(nlog2n) 。

80 pts

或许并不需要限制函数的作用区间。

比如说前两个函数,由于直线的斜率一定为负,所以在 x≤ai−dix\leq a_i-d_ix≤ai​−di​ 时,直线本就比抛物线更优。同理,抛物线在 x∈(ai−di,ai+di)x\in(a_i-d_i,a_i+d_i)x∈(ai​−di​,ai​+di​) 时比直线更优。

如果只有这前两个函数,就可以直接添加进全区间,复杂度就保证是 O(nlog⁡n)O(n\log n)O(nlogn) 。

100 pts

我们看第三个函数,一个向上的抛物线,把它补全的话,前面一定比前两个函数高了。

但是我们可以人工构造一个分段函数
Ci(x)={−ki(ai−di)+bi(x<ai+di)−ki(ai−di)+bi+(x−(ai+di))2(x≥ai+di)C_i(x)=\begin{cases} -k_i(a_i-d_i)+b_i &~(x<a_i+d_i)\\ -k_i(a_i-d_i)+b_i+(x-(a_i+d_i))^2~~&~(x\geq a_i+d_i) \end{cases} Ci​(x)={−ki​(ai​−di​)+bi​−ki​(ai​−di​)+bi​+(x−(ai​+di​))2  ​ (x<ai​+di​) (x≥ai​+di​)​

而这个函数是可以用李超树维护的。

当然,这个分段函数怎么构造都行,满足条件就够了,比如 std 就是添加了一条斜率为 1 的直线(定义在整数域上的该二次函数可以认为底部导数为 1 )。

所以,令
Ai(x)=−kix+bi,Bi(x)=−ki(ai−di)+bi+di2−(ai−x)2A_i(x)=-k_ix+b_i~~,~~B_i(x)=-k_i(a_i-d_i)+b_i+d_i^2-(a_i-x)^2 Ai​(x)=−ki​x+bi​  ,  Bi​(x)=−ki​(ai​−di​)+bi​+di2​−(ai​−x)2

那么 fi(x)=max⁡{Ai(x),Bi(x),Ci(x)}f_i(x)=\max\{A_i(x),B_i(x),C_i(x)\}fi​(x)=max{Ai​(x),Bi​(x),Ci​(x)} 。

DP 的转移式就可以变成
dp[i]=max⁡j<i{dp[j]+Aj+1′(i),dp[j]+Bj+1′(i),dp[j]+Cj+1′(i)}dp[i]=\max_{j<i}\{dp[j]+A'_{j+1}(i),dp[j]+B'_{j+1}(i),dp[j]+C'_{j+1}(i)\} dp[i]=j<imax​{dp[j]+Aj+1′​(i),dp[j]+Bj+1′​(i),dp[j]+Cj+1′​(i)}

我们单独维护三种函数的“凸包”,时间复杂度 O(nlog⁡n)O(n\log n)O(nlogn) 。

法 2

观察到 (r−l+1)2=r2+(l−1)2−2(l−1)r(r-l+1)^2=r^2+(l-1)^2-2(l-1)r(r−l+1)2=r2+(l−1)2−2(l−1)r ,我们把 r2r^2r2 甩到 max⁡{}\max\{\}max{} 外面,可以把二次项都除掉,它就变成了一个一次函数!

于是我们直接拿三个李超树维护这三段一次函数,

由于第三段直接加会影响到前两段,但是第三段的作用区间是 [l−1+al+dl,n][l-1+a_l+d_l,n][l−1+al​+dl​,n] ,而我们做DP是从左到右的,所以我们可以在 l−1+al+dll-1+a_l+d_ll−1+al​+dl​ 处打个标记,扫到了那里再加第三段。

时间复杂度 O(nlog⁡n)O(n\log n)O(nlogn) ,各方面吊打标算。

CODE(std)

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<queue>
#include<stack>
#include<random>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<unordered_map>
#pragma GCC optimize(2)
using namespace std;
#define MAXN 1000005
#define LL long long
#define ULL unsigned long long
#define ENDL putchar('\n')
#define DB double
#define lowbit(x) (-(x) & (x))
#define FI first
#define SE second
#define PR pair<LL,int>
int xchar() {static const int maxn = 1000000;static char b[maxn];static int pos = 0,len = 0;if(pos == len) pos = 0,len = fread(b,1,maxn,stdin);if(pos == len) return -1;return b[pos ++];
}
#define getchar() xchar()
LL read() {LL f = 1,x = 0;int s = getchar();while(s < '0' || s > '9') {if(s<0)return -1;if(s=='-')f=-f;s = getchar();}while(s >= '0' && s <= '9') {x = (x<<1) + (x<<3) + (s^48);s = getchar();}return f*x;
}
void putpos(LL x) {if(!x)return ;putpos(x/10);putchar((x%10)^48);}
void putnum(LL x) {if(!x) {putchar('0');return ;}if(x<0) putchar('-'),x = -x;return putpos(x);
}
void AIput(LL x,int c) {putnum(x);putchar(c);}int n,m,s,o,k;
int a[MAXN],b[MAXN],ki[MAXN],d[MAXN];
inline LL Max(LL a,LL b) {return a>b ? a:b;}
struct it{LL a,b,c;LL k,d;int ad;it(){a=b=k=ad=0;c=d=-5e18;}LL F(int x) {if(x <= ad) return k*x+d;return a*x*x + b*x + c;}
}tre[MAXN*6];
int ls[MAXN*6],rs[MAXN*6],cnt;
void addtree(int &a,int al,int ar,it y) {if(!a) tre[a=++cnt] = it(),ls[cnt]=rs[cnt]=0;LL l1 = tre[a].F(al),r1 = tre[a].F(ar);LL l2 = y.F(al),r2 = y.F(ar);if(l1 >= l2 && r1 >= r2) return ;if(l1 <= l2 && r1 <= r2) {tre[a]=y;return ;}int md = (al + ar) >> 1;if(tre[a].F(md) <= y.F(md)) swap(tre[a],y),swap(l1,l2);if(l1 >= l2) addtree(rs[a],md+1,ar,y);else addtree(ls[a],al,md,y);return ;
}
LL findtree(int a,int x,int al,int ar) {if(al > x || ar < x || !a) return (LL)-5e18;LL res = tre[a].F(x); int md = (al + ar) >> 1;if(al == ar) return res;return Max(res,Max(findtree(ls[a],x,al,md),findtree(rs[a],x,md+1,ar)));
}
LL dp[MAXN];
int main() {freopen("blackbridge.in","r",stdin);freopen("blackbridge.out","w",stdout);n = read();dp[0] = 0;int r1 = 0,r2 = 0,r3 = 0;for(int i = 1;i <= n;i ++) {ki[i] = read();a[i] = read(); b[i] = read(); d[i] = read();it s1,s2,s3;s1.k = -ki[i]; s1.d = dp[i-1] + b[i] + ki[i]*1ll*(i-1);s1.ad = n+1;s2.a = -1; s2.ad = 0;s2.c = -ki[i]*1ll*(a[i]-d[i]) + b[i] + dp[i-1] + d[i]*1ll*d[i] - (a[i]+i-1)*1ll*(a[i]+i-1);s2.b = (a[i]+i-1)*2;s3.a = 1; s3.ad = a[i] + d[i] + i-1;s3.b = -2ll*(a[i]+d[i]+i-1);s3.c = -ki[i]*1ll*(a[i]-d[i]) + b[i] + dp[i-1] + s3.ad*1ll*s3.ad;s3.d = -ki[i]*1ll*(a[i]-d[i]) + b[i] - s3.ad + dp[i-1];s3.k = 1;addtree(r1,1,n,s1);addtree(r2,1,n,s2);addtree(r3,1,n,s3);dp[i] = Max(Max(findtree(r1,i,1,n),findtree(r2,i,1,n)),findtree(r3,i,1,n));}AIput(dp[n],'\n');return 0;
}

【NOI模拟赛】黑色大桥(DP优化,李超树)相关推荐

  1. Newnode‘s NOI 模拟赛 第二题 (单调dp)

    第二题 问题描述 样例输入 1 3 2 *# #* ## 样例输出 1 2 样例输入 2 4 5 *#### *#### *#### #* * * # 样例输出 2 3 提示 对于20%的数据n,m& ...

  2. 【NOI模拟赛】纸老虎博弈(博弈论SG函数,长链剖分)

    题面 某天,C 和 K 觉得很无聊,于是决定玩一个经典小游戏: 在一棵有 nnn 个结点的有根树上,标号为 iii 的节点上有 aia_iai​ 个棋子.游戏时玩家轮流操作,每次可以将任意一个节点 u ...

  3. 【NOI模拟赛】摆(线性代数,杜教筛)

    题面 6s , 1024mb 我是XYX,我擅长摆. 我在摆大烂的时候看到一个 n × n n\times n n×n 的矩阵 A A A : A i , j = { 1 i = j 0 i ≠ j ...

  4. [2020.11.25NOIP模拟赛]下棋【dp】

    正题 题目链接:https://www.luogu.com.cn/problem/U142297?contestId=37766 题目大意 nnn个白棋mmm个黑棋排成一排,要求没有任何一段黑白棋的个 ...

  5. [2020.11.25NOIP模拟赛]出租车【dp】

    正题 题面链接:https://www.luogu.com.cn/problem/U142298?contestId=37766 题目大意 nnn个人有起点和终点,按顺序上车,但下车可以任意顺序,车最 ...

  6. 【NOI模拟赛】Froggy Problem(数据结构,线段树)

    题目背景

  7. 【NOI模拟赛】最小生成树(kruskal算法,线段树合并)

    题面 (一道原创题,我直接扒图了 时限:2s,空间:1GB 题解 为了方便,下文认为 n , m , q n,m,q n,m,q 同阶.同样,不妨给每条边强制定大小,使得不存在两条边边权相等. 有一个 ...

  8. 【洛谷】NOIP提高组模拟赛Day2【动态开节点/树状数组】【双头链表模拟】

    U41571 Agent2 题目背景 炎炎夏日还没有过去,Agent们没有一个想出去外面搞事情的.每当ENLIGHTENED总部组织活动时,人人都说有空,结果到了活动日,却一个接着一个咕咕咕了.只有不 ...

  9. 「模拟赛20180307」三元组 exclaim 枚举+树状数组

    题目描述 给定 \(n,k\) ,求有多少个三元组 \((a,b,c)\) 满足 \(1≤a≤b≤c≤n\)且\(a + b^2 ≡ c^3\ (mod\ k)\). 输入 多组数据,第一行数据组数\ ...

最新文章

  1. 练习10-1 使用递归函数计算1到n之和 (10 分)
  2. Centos6.X升级glibc解决“libc.so.6 version GLIBC_2.14 not found”报错问题
  3. php oauth api,PHP实现人人OAuth登录和API调用
  4. vb.net2019- 对象 (Visual Basic)
  5. 使用java多线程分批处理数据工具类
  6. 国家开放大学2021春1338幼儿园教育质量评价题目
  7. 编码原则:意外情况的注释
  8. RESTful学习笔记
  9. day3-python之函数初识(二)
  10. AMP+EPP3.0的开发环境配置
  11. 如何在doc下运行java程序及打包成.jar格式的Java程序
  12. 蓝牙耳机出口欧盟要做什么CE的哪些指令?
  13. viper4android最新版卡刷包,viper4android音效驱动-ViPER4Android 安卓版v2.3.4.0-PC6安卓网...
  14. ETL数据抽取 全量 增量
  15. OSPF认证、虚链路、过滤、track
  16. 经典古诗词名句 mysql_经典古诗词名句摘抄50句
  17. 求两个数的最大公约数(C++)
  18. python的安装框架acode_Python安装与入门
  19. 在网页前端调用exe程序并传参
  20. Javaweb安全——JSP Webshell

热门文章

  1. 压缩为王-阿里第五届中间件复赛总结
  2. IT研发人员的四种工作
  3. 【建议收藏】2020年中高级Android大厂面试秘籍,为你保驾护航金三银四,直通大厂(Android高级篇下)...
  4. 出行助手Vue项目中高德API的使用
  5. 桌面程序聊天窗口的自动输入内容及自动发送
  6. google 招聘题
  7. 【Linux rar,unrar命令安装详细实践】
  8. INSERT INTO 小技巧
  9. opencv h264压缩视频
  10. mysql prefix_mysql改变innodb_large_prefix