P3991 [BJOI2017] 喷式水战改
P3991 [BJOI2017] 喷式水战改
题目背景
拿到了飞机的驾照(?),这样补给就不愁了
XXXX年XX月XX日
拿到了喷气机(??)的驾照,这样就飞得更快了
XXXX年XX月XX日
拿到了攻击机(???)的驾照(不存在的)
XXXX年XX月XX日
用铅版做夹层的话,机身可是会变重的呢
XXXX年XX月XX日
幸酱的特制快递,精确投递到了目标地点
又是核平的一天。
天音正在给喷气机做保养,并充填燃料。
这种喷气机是某姬(?????)特别制作的,发动机拥有三种工作状态
1、通常型(Original):在高空平飞或隐蔽飞行时进行的低功耗高效率工作状态
2、后期型(Extended):为在俯冲时最大化能量利用率而特别改造过的工作状态
3、增强型(Enhanced):在俯冲攻击结束后为产生极限扭力抬高高度的工作状态
在一次攻击中,喷气机将会经历"通常-后期-增强-通常"的工作流程
不同工作状态中,燃料的利用效率是不同的
现在天音正在调整喷气机燃料装填序列
你需要做的就是求出燃料能产生的最大总能量
为什么是你?
和平还是核平,选一个吧
题目描述
初始燃料序列为空。每次操作会向序列中的
添加的位置
全部的
对于一个确定的燃料序列,其能产生的最大总能量为:将序列依次分成"通常-后期-增强-通常"四段(每段可以为空),每一段在对应工作状态下产生的能量之和的最大值。
对于每次添加操作,你需要给出该次操作使得最大总能量增加了多少。
如果对于这种计算方式没有直观的感受,可以查看样例说明。
输入格式
第一行一个数
接下来
这种燃料每单位在通常、后期、增强工作状态下产生的能量分别为
输出格式
对于
对于
后
Solution:
果然还是太久没写 dp 了,连这么简单的转移都忘了。(也有可能是看见平衡树标签太忘乎所以导致的) (。í _ ì。)
先说最大化能量:
我们将一个同颜色连续段看成一个点。
假设我们现在要合并两个区间和一个点
我们将四个阶段记录为
我们记数组
其中
所以我们只需要在pushup的时候花费
实现:
那么如何实现插入一个颜色连续段呢,那么当然是平衡树了,我们维护一颗平衡树,按照连续段数来分裂,查询节点总数就从
Code:
#include<bits/stdc++.h> #define int long long #define For(i,l,r) for(int i=l;i<=r;i++) const int N=5e5+5; using namespace std; inline int rd(){return rand()*rand()+17;} int f[N][4][4]; int cnt,rt,n,m,ans; struct Tree{ int ls,rs,siz,len,sum,val[4],pri; }t[N]; void debug(int x) { printf("Node: %lld: %lld %lld %lld %lld %lld %lld %lld %lld %lld %lld\n",x,t[x].ls,t[x].rs,t[x].siz,t[x].len,t[x].sum,t[x].val[0],t[x].val[1],t[x].val[2],t[x].val[3],t[x].pri); } void upd(int x) { memset(f[x],0,sizeof(f[x])); For(i,0,3)For(j,i,3)For(k,j,3) f[x][i][k]=max(f[x][i][k],f[t[x].ls][i][j]+t[x].val[j]*t[x].len+f[t[x].rs][j][k]); t[x].sum=t[t[x].ls].sum+t[t[x].rs].sum+t[x].len; t[x].siz=t[t[x].ls].siz+t[t[x].rs].siz+1; } int Node(int siz,int x,int a,int b,int c) { t[++cnt]={0,0,siz,x,x,{a,b,c,a},rd()};upd(cnt);return cnt; } void splite(int x,int &a,int &b,int k) { if(!x){a=b=0;return;}int tmp=t[t[x].ls].siz+1; if(tmp<=k){a=x;splite(t[x].rs,t[a].rs,b,k-tmp);} else {b=x;splite(t[b].ls,a,t[b].ls,k);} upd(x); } int merge(int x,int y) { if(!x||!y)return x|y; if(t[x].pri<=t[y].pri){t[x].rs=merge(t[x].rs,y);upd(x);return x;} t[y].ls=merge(x,t[y].ls);upd(y);return y; } int find(int k) { int x=rt,res=0; while(x) { if(k<=t[t[x].ls].sum)x=t[x].ls; else if(k<=t[t[x].ls].sum+t[x].len)return res+t[t[x].ls].siz+1; else k-=t[t[x].ls].sum+t[x].len,res+=t[t[x].ls].siz+1,x=t[x].rs; } return res; } void work() { cin>>n; for(int i=1,p,x,a,b,c;i<=n;i++) { scanf("%lld%lld%lld%lld%lld",&p,&a,&b,&c,&x); int pos=find(p),l,mid,r,len_l,len_r,u=Node(1,x,a,b,c); splite(rt,l,r,pos);splite(l,l,mid,pos-1); len_l=p-t[l].sum,len_r=t[l].sum+t[mid].sum-p; if(t[l].sum+t[mid].len==p)rt=merge(merge(l,mid),merge(u,r)); else { a=t[mid].val[0],b=t[mid].val[1],c=t[mid].val[2]; int lp=Node(mid,len_l,a,b,c);int rp=Node(mid,len_r,a,b,c); rt=merge(merge(l,merge(lp,u)),merge(rp,r)); } printf("%lld\n",f[rt][0][3]-ans);ans=f[rt][0][3]; } } #undef int int main() { srand(998244353); //freopen("P3991.in","r",stdin); //freopen("P3991.out","w",stdout); work(); return 0; }