LOJ#6515. 「雅礼集训 2018 Day10」贪玩蓝月题解

贪玩蓝月

一个朴素的想法就是模拟这个过程,当询问时做一遍01背包,但这样明显会超时
想象这样一个例子:当两次询问中间夹着一次插入操作
第二次进行01背包,明显只需要在第一次的基础上对新插入的数做一次01背包
由于 \(p\) 很小,所以这样时间复杂度大大减小
所以我们可以对前端插入与后端插入分别维护一个栈,每次插入时对新插入的数做一次01背包
删除时让栈顶减一即可,因为再次插入时会覆盖这个值
那么当一端已经为 \(0\) 时,再有对该端的删除操作怎么办?
此时将另一端的前一半暴力加入该段即可(另一端也要暴力维护一下)
那么如何查询呢?
枚举一端可能的特征值 \(i\) ,令 \(L=(l-i+p)%p\)\(R=(r-i+p)%p\)
则另一端可能的特征值在 \(L\)\(R(R>=L)\)\(0\)\(R\)\(L\)\(p-1(R<L)\) 之间
用ST表维护区间最值即可

#include<bits/stdc++.h>
using namespace std;

const int M=50004,P=505;

int f[M][P][5];//背包
int stat[M][5],stag[M][5];//特征值 战斗值
int top[5];//栈顶
int m,p;
char a[10];

void add(int d,int x,int y){//加入
    int to=++top[d];
    stat[to][d]=x;stag[to][d]=y;
    for(int i=0;i<p;i++){//进行一次背包
        if(i>=x) f[to][i][d]=f[to-1][i-x][d]+y;
        else f[to][i][d]=f[to-1][p+i-x][d]+y;
        f[to][i][d]=max(f[to-1][i][d],f[to][i][d]);
    }
}

int st[M][50];//ST表
void init(int d){//ST表初始化
    for(int i=0;i<p;i++) st[i][0]=f[top[d]][i][d];
    for(int k=1;k<=10;k++)
        for(int i=0;i+(1<<k)-1<p;i++)
            st[i][k]=max(st[i][k-1],st[i+(1<<(k-1))][k-1]);
}

int get(int l,int r){//ST表查询区间最大值
    int k=0;
    while(l+(1<<(k+1))-1<=r) k++;
    return max(st[l][k],st[r+1-(1<<k)][k]);
}

int ask(int l,int r){//询问
    int d=0,ma=-1;
    if(top[0]==0&&top[1]==0){//都为空
        if(l==0) return 0;
        else return -1;
    }
    if(top[d]==0) d=d^1;//令d不为空
    init(d^1);
    for(int i=0;i<p;i++){
        if(f[top[d]][i][d]<0) continue;

        int L=(l-i+p)%p,R=(r-i+p)%p,maxn;
        if(L<=R) maxn=get(L,R);
        else maxn=max(get(L,p-1),get(0,R));

        if(maxn<=0&&(i<l||i>r)) continue;//如果另一边为空且当前的特征值不在范围则舍去

        ma=max(ma,f[top[d]][i][d]+maxn);
    }
    return ma;
}

int main(){
    int T;scanf("%d", &T);

    scanf("%d %d", &m, &p);

    for(int i=0;i<=m;i++) for(int j=1;j<p;j++) for(int k=0;k<=1;k++) f[i][j][k]=-1e10;//背包初始化

    for(int iii=1;iii<=m;iii++){
        int x,y,d=0;
        scanf("%s", a+1);
        if(a[2]=='G') d=1;//0为左 1为右

        if(a[1]=='I'){//加
            scanf("%d %d", &x ,&y);
            add(d,x%p,y);
        }

        else if(a[1]=='D'){//减
            if(top[d]==0){
                int mid=(top[d^1]+1)/2,to=top[d^1];top[d^1]=0;//(top[d^1]+1)/2是因为top[d^1]可能等于1
                for(int i=mid;i;i--) add(d,stat[i][d^1],stag[i][d^1]);
                for(int i=mid+1;i<=to;i++) add(d^1,stat[i][d^1],stag[i][d^1]);
            }
            top[d]--;
        }

        else{//查询
            scanf("%d %d", &x, &y);
            printf("%d\n", ask(x,y));
        }
    }
    return 0;
}
posted @ 2023-09-09 22:12  Idtwtei  阅读(73)  评论(0编辑  收藏  举报