• bzoj4906: [BeiJing2017]喷式水战改


    Description

    【题目背景】
    拿到了飞机的驾照(?),这样补给就不愁了
    XXXX年XX月XX日
    拿到了喷气机(??)的驾照,这样就飞得更快了
    XXXX年XX月XX日
    拿到了攻击机(???)的驾照(不存在的)
    XXXX年XX月XX日
    用铅版做夹层的话,机身可是会变重的呢
    XXXX年XX月XX日
    幸酱的特制快递,精确投递到了目标地点
    -------------------------------------
    又是核平的一天。
    天音正在给喷气机做保养,并充填燃料。
    这种喷气机是某姬(?????)特别制作的,发动机拥有三种工作状态
    1、通常型(Original):在高空平飞或隐蔽飞行时进行的低功耗高效率工作状态
    2、后期型(Extended):为在俯冲时最大化能量利用率而特别改造过的工作状态
    3、增强型(Enhanced):在俯冲攻击结束后为产生极限扭力抬高高度的工作状态
    在一次攻击中,喷气机将会经历"通常-后期-增强-通常"的工作流程
    不同工作状态中,燃料的利用效率是不同的
    现在天音正在调整喷气机燃料装填序列
    你需要做的就是求出燃料能产生的最大总能量
    为什么是你?
    和平还是核平,选一个吧
     
    【题目描述】
    初始燃料序列为空。每次操作会向序列中的pi位置添加xi单位的同种燃料,该燃料每一单位在三种工作状态下能产
    生的能量分别为ai, bi, ci。添加的位置pi是指,在添加后,加入的第一个单位燃料前面有pi个单位的原燃料。全
    部的xi单位燃料依次放置,然后原来在pi位置的燃料(如果有的话)依次向后排列。对于一个确定的燃料序列,其
    能产生的最大总能量为:将序列依次分成"通常-后期-增强-通常"四段(每段可以为空),每一段在对应工作状态
    下产生的能量之和的最大值。对于每次添加操作,你需要给出该次操作使得最大总能量增加了多少。如果对于这种
    计算方式没有直观的感受,可以查看样例说明。

    Input

    第一行一个数n,表示操作个数。
    接下来n行,每行5个数pi, ai, bi, ci, xi,空格分隔,表示向序列中的pi位置添加xi单位的同种燃料
    这种燃料每单位在通常、后期、增强工作状态下产生的能量分别为ai, bi, ci。
    对于100%的数据,1 ≤ n ≤ 10^5, 1 ≤ ai, bi, ci ≤ 10^4, 1 ≤ xi ≤ 10^9。
    对于100%的数据,保证插入时序列中至少已有pi单位的燃料。

    Output

    n行,每行一个数,表示该次操作后能量序列所能产生的最大总能量增加了多少。

    Sample Input

    5
    0 25 37 46 2
    1 32 14 16 3
    3 99 77 88 4
    2 43 68 57 5
    14 72 36 18 6

    Sample Output

    92
    75
    396
    319
    432

    【样例解释】
    第一次操作后,燃料序列为[1 1],最大能量发生方式为[En1 En1],共46+46=92。
    第二次操作后,燃料序列为[1 2 2 2 1],最大能量发生方式为[Or1 Or2 Or2 Or2 En1],共25+32+32+32+46=167,增加了167-92=75。
    第三次操作后,燃料序列为[1 2 2 3 3 3 2 1],最大能量发生方式为[Or1 Or2 Or2 Or3 Or3 Or3 Or3 Or2 En1],增加了99*4=396。
    第四次操作后,燃料序列为[1 2 4 4 4 4 4 2 3 3 3 2 1]
    最大能量发生方式为[Or1 Or2 Ex4 Ex4 Ex4 Ex4 Ex4 Or2 Or3 Or3 Or3 Or3 Or2 Or1]。
    第五次操作后,燃料序列为[1 2 4 4 4 4 4 2 3 3 3 2 1 5 5 5 5 5 5]
    最大能量发生方式为[Or1 Or2 Ex4 Ex4 Ex4 Ex4 Ex4 Or2 Or3 Or3 Or3 Or3 Or2 Or1 Or5 Or5 Or5 Or5 Or5 Or5]。
     
    题解:
    首先很明显能看出来,求最大能量需要用到dp。
    显然,某一连续相同种类的燃料段,在满足最终为最大能量时,一定是内部所有燃料在同种工作状态下的
    dp转移第一版:dp[i][j] (0<=j<3) 表示到第i个燃料段,第i个燃料段工作状态<=j可产生的最大能量。
        伪方程:dp[i][j]=max(dp[i-1][j]+该工作状态j产生的单位能量*段长度,dp[i][j-1])
     
    题目中有插入操作,需要用数据结构维护——平衡树。
    我们把每一段加进来的燃料作为一个节点拆入平衡树中。
    根据插入位置,这个节点可能插在某个原来的“燃料段”中间,那我们就需要把那个燃料段拆成两半,把这个节点插到中间
    所以选择,到底用哪个平衡树?
    这个平衡树需要知道“排名”,需要支持不断删点、加点的操作——SPLAY!
     
    这时我们发现,如果按之前的dp方式,每新加一个点便要O(n)求一遍,总复杂度O(n²)
    这可不太妙。。。
    于是我们需要探索一种新的dp方式
    我们可以考虑将dp于splay结合在一起,或是说在splay的时候顺便dp
    那么dp转移第二版就出来了:对于splay中每一个节点,dp[i][j] (0<=i,j<4) 表示该节点及子树这一大段中的工作状态在i~j之间(或者可以这样理解:这段中最左边的燃料段的工作状态>=i,最右边的燃料段工作状态<=j,整个大段满足题目要求)
        在每次update时维护即可。
    这样感觉很巧妙有木有!
     
    注意:我曾被卡常了好几次。。。就是dp[i][j]的含义问题:如果如上面叙述那样表示,每次转移复杂度大约4³;可如果表示这段中最左边燃料段工作状态就是i,最右边的就是j,那每次转移的复杂度就是44了…所以有句话说得好:dp状态想对了就是成功了一半
     
    代码:
      1 #include<cstdio>
      2 #include<iostream>
      3 #include<algorithm>
      4 using namespace std;
      5 
      6 typedef long long ll;
      7 const int N = 100005;
      8 struct node{
      9     int v,a,b,c;
     10     ll s[4][4],dp[4][4],len;
     11     node *pa,*ch[2];
     12 }pool[2*N],*root,*rf;
     13 int cnt;
     14 
     15 inline ll len(node *p) { return p?p->len:0; }
     16 ll c[4][4];
     17 void merge(ll a[4][4],ll b[4][4],node *p){
     18     for(int i=0;i<4;i++)
     19         for(int j=i;j<4;j++){
     20             c[i][j]=0;
     21             for(int k=i;k<=j;k++)
     22                 c[i][j]=max(c[i][j],a[i][k]+b[k][j]);     
     23         }
     24     for(int i=0;i<4;i++)
     25         for(int j=0;j<4;j++) p->dp[i][j]=c[i][j];
     26 }
     27 void update(node *p){
     28     p->len=(ll)(len(p->ch[0])+len(p->ch[1])+p->v);
     29     for(int i=0;i<4;i++)
     30         for(int j=0;j<4;j++) p->dp[i][j]=p->s[i][j];
     31     if(p->ch[0]) merge(p->ch[0]->dp,p->dp,p);
     32     if(p->ch[1]) merge(p->dp,p->ch[1]->dp,p);
     33 }
     34 void ups(node *p){
     35     p->dp[0][0]=p->dp[3][3]=p->s[0][0]=p->s[3][3]=(ll)p->v*p->a;  
     36     p->dp[1][1]=p->s[1][1]=(ll)p->v*p->b;
     37     p->dp[2][2]=p->s[2][2]=(ll)p->v*p->c;
     38     for(int i=0;i<4;i++)
     39         for(int j=i+1;j<4;j++)
     40             p->dp[i][j]=p->s[i][j]=max(p->s[i][j-1],p->s[j][j]);
     41 }
     42 
     43 void rotate(node *p,int t){
     44     node *pa=p->pa,*gp=pa->pa,*son=p->ch[t^1];
     45     pa->ch[t]=son; if(son) son->pa=pa;
     46     p->ch[t^1]=pa; pa->pa=p;
     47     p->pa=gp; gp->ch[pa==gp->ch[1]]=p;
     48     if(root==pa) root=p;
     49     update(pa);update(p);
     50 }
     51 void splay(node *p,node *t){
     52     while(p->pa!=t){
     53         if(p->pa->pa==t)
     54             rotate(p,p==p->pa->ch[1]);
     55         else{
     56             node *pa=p->pa,*gp=pa->pa;
     57             int f=pa==gp->ch[0];
     58             if(p==pa->ch[f]) rotate(p,f),rotate(p,!f);
     59             else rotate(pa,!f),rotate(p,!f);     
     60         }
     61     } 
     62 }
     63 
     64 node *find(node *p,ll k){
     65     if(len(p->ch[0])>=k) return find(p->ch[0],k);
     66     if(len(p->ch[0])+p->v>=k) return p;
     67     return find(p->ch[1],k-p->v-len(p->ch[0]));   
     68 }
     69 void addnew(node *p,int a,int b,int c,int v){
     70     p->a=a;p->b=b;p->c=c;p->v=v;p->len=(ll)v;
     71     ups(p);
     72 }
     73 void insert(ll k,node *nd){
     74     node *p=find(root,k),*q;
     75     splay(p,rf);
     76     if(root->len==k) {
     77         p->ch[1]=nd; nd->pa=p;
     78         update(p);
     79         return;
     80     }
     81     q=find(root,k+1);
     82     if(p!=q){
     83         splay(q,root);
     84         q->ch[0]=nd; nd->pa=q;
     85         update(q); update(p);
     86         return;
     87     }
     88     q=p->ch[1];
     89     if(!q){
     90         p->ch[1]=nd; nd->pa=p;
     91         q=&pool[++cnt]; addnew(q,p->a,p->b,p->c,(int)(len(p->ch[0])+p->v-k));
     92         nd->ch[1]=q; q->pa=nd;
     93         p->v=p->v-q->v; ups(p);
     94         update(nd);update(p);
     95     }
     96     else{
     97         while(q->ch[0]) q=q->ch[0];
     98         splay(q,root);
     99         node *tmp=&pool[++cnt]; addnew(tmp,p->a,p->b,p->c,(int)(len(p->ch[0])+p->v-k));
    100         q->ch[0]=nd; nd->pa=q;
    101         nd->ch[1]=tmp; tmp->pa=nd;
    102         p->v=p->v-tmp->v; ups(p);
    103         update(tmp);update(nd);update(q);update(p);     
    104     }
    105 }
    106 
    107 int n;
    108 
    109 int main()
    110 {
    111     int a,b,c,x,i,j;
    112     ll pos,last=0,now;
    113     scanf("%d",&n);
    114     
    115     rf=&pool[++cnt];
    116     while(n--){
    117         scanf("%lld%d%d%d%d",&pos,&a,&b,&c,&x);
    118         node *nd=&pool[++cnt]; addnew(nd,a,b,c,x);
    119         if(!root) {
    120             root=nd;
    121             root->pa=rf;rf->ch[1]=root;
    122         }
    123         else insert(pos,nd);
    124         
    125         now=root->dp[0][3];
    126         printf("%lld
    ",now-last);
    127         last=now;
    128     }
    129 
    130     return 0;    
    131 }
    View Code
    既然选择了远方,便只顾风雨兼程
  • 相关阅读:
    TypeScript教程
    Xcode使用介绍之一:Xcode简介+创建App应用
    VSCode
    vscode 配置import @ 路径提示及代码智提
    踩坑:VScode 集成 eslint 插件
    VSCode配置ESLint
    如何安装Vscode软件及设置成中文界面?
    vscode 插件推荐
    1-VScode格式化ESlint-方法(最全最好用方法!)
    HTML5实践之歌词同步播放器
  • 原文地址:https://www.cnblogs.com/lindalee/p/7989878.html
Copyright © 2020-2023  润新知