1 条题解

  • 0
    @ 2025-8-24 22:30:32

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 囧仙
    你做东方鬼畜音MAD,好吗?

    搬运于2025-08-24 22:30:32,当前版本为作者最后更新于2021-03-23 20:57:45,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    这题使用了一个非常简单的 trick\text{trick} ,但我相信大部分人都能想得到的()

    题解

    一眼扫过该题,你会发现它是个背包板子。先不考虑其他问题,让我们想想朴素的背包做法怎么做。

    如果你接触过对顶堆一类的简单科技,你会发现这题相当于将两个背包问题拼接在了一起。你通过聪明才智实现了一个背包类,结构就像是一个堆栈。它支持在顶部插入一种物品,弹出顶部的那种物品,以及快速查询,一个空间为 v0v_0 的背包能装下的最大价值是多少。简要的实现办法如下:

    • dpi,jdp_{i,j} 表示前 ii 个物品,使用不超过 jj 的空间可以获得的最大价值。

    • 现在加入了一个物品 (vi,wi,ti)(v_i,w_i,t_i) ,表示它的体积为 viv_i ,价值为 wiw_i ,类型为 tit_i (只有一个,还是有无穷个)。那么转移方程如下:

    $$dp_{u,i}=\max\{dp_{u-1,i},dp_{u,i},dp_{u,i-v_i}+w_i\} $$

    它的枚举顺序与 tit_i 相关。其中, uu 表示当前这个物品序列中有多少个物品。

    • 删除一个物品就更简单了,你只需要 uu1u\gets u-1 就行了。

    回到正题,你现在实现了这样的背包类,然后实例化了两个,不妨称为 Bag1,Bag2Bag_1,Bag_2 。考虑如何完成题目中要求的五种操作。使用类似对顶堆的结构,两个背包的顶端相对。

    • 机械臂向右移动。你只要弹出 Bag2Bag_2 顶端的物品,然后插入到 Bag1Bag_1 顶端就行了。

    • 机械臂向左移动。你只要弹出 Bag1Bag_1 顶端的物品,然后插入到 Bag2Bag_2 顶端就行了。

    • 插入一个物品。这一部分也很简单,你直接向 Bag2Bag_2 顶部插入一个物品就行了。

    • 删除一个物品。与插入物品类似,改为移除 Bag2Bag_2 顶部的物品。

    • 修改一个物品。比较简单的做法就是直接弹出 Bag2Bag_2 顶部,然后再插入一个物品。

    你兴致勃勃地实现了所有操作,然后你发现空间是 O(vq)\mathcal O(vq) 的。于是很幸运地,你 MLE\text{MLE} 了。下面考虑如何优化这个 BagBag 类。


    如果你接触过简单背包问题,那么你应该知道滚动数组优化。可惜的是,这题因为需要支持弹出操作,而滚动数组会丢失掉很多信息,所以朴素的滚动数组是会出锅的。但这不妨是一个很好的启发。

    根据某些奇妙的知识(比如学过\sout\text{ Splay}),你就能自然想到把可能访问到的东西放在旁边,用来加速它的访问速度。这题也是如此。

    由于操作 1,21,2 的存在,于是每次操作的位置都是相邻的。考虑记录 u[l,r]u\in[l,r] 时的所有 dpdp 信息,那么当 uu 在这个范围内游走时就可以直接取出对应的 dpdp 数组了。我们不能无限制开大 rlr-l 的大小,于是不妨设 s=rls=r-l ,其中 ss 是常数。这部分空间复杂度是 O(vs)\mathcal O(vs) 的。下面考虑 uu 跃出这个范围应该如何处理。

    背包问题有个非常好的性质,当你知道了 dpu,x(x[0,v])dp_{u,x}(x\in[0,v]) ,以及它后面一些物品的属性(大小、价值),你就能快速生成 dpu+1,x,dpu+2,xdp_{u+1,x},dp_{u+2,x}\cdots 。不妨记录 u0(mods)u\equiv 0\pmod{s'} 时的 dpu,xdp_{u,x} 作为关键点,其中 ss' 是常数。当 uu 跨出 [l,r][l,r] 时就对它进行调整(比如, u<lu<l 时就 lls,rrsl\gets l-s',r\gets r-s' ),然后花费 O(vs)\mathcal O(vs) 的时间重新生成这一部分的 dpdp 数组。下面考虑确定这些常数具体的值。

    初始时,显然 l=0,r=sl=0,r=s 。我们希望每次更新 l,rl,r ,都使得 uul,rl,r 的中点,这样子到下一次更新需要更新 uu 的次数最少。于是令 s=12ss'=\frac{1}{2}s 。每次更新 l,rl,r ,时间复杂度是 O(vs)\mathcal O(vs) 。最坏情况下要更新 qs\frac{q}{s'} 次,于是时间复杂度是 O(vq)\mathcal O(vq) ,这是没有问题的。关键点的数目是 qs\frac{q}{s'} ,每个关键点都要维护 O(v)\mathcal O(v) 的空间,这部分空间是 O(vqs)\mathcal O(v\cdot \frac{q}{s'}) 。同时维护 [l,r][l,r] 也要花费 O(vs) \mathcal O(vs) ,所以总共花费的空间复杂度是 O(v(qs+s))\mathcal O(v\cdot(\frac{q}{s'}+s)) ,取 s=q2s=\sqrt{\frac{q}{2}} 可以得到最小的空间复杂度。

    总时间复杂度 O(vq)\mathcal O(vq) ,空间复杂度 O(vq)\mathcal O(v\sqrt q) ,可以通过本题。

    参考代码

    #include<bits/stdc++.h>
    #define up(l,r,i) for(int i=l,END##i=r;i<=END##i;++i)
    #define dn(r,l,i) for(int i=r,END##i=l;i>=END##i;--i)
    using namespace std;
    typedef long long i64;
    int qread(){
        int w=1,c,ret;
        while((c=getchar())> '9'||c< '0') w=(c=='-'?-1:1); ret=c-'0';
        while((c=getchar())>='0'&&c<='9') ret=ret*10+c-'0';
        return ret*w;
    }
    const int MAXN =2e4+3,MAXM=175+3,MAXQ=3e4+3,SI=4;
    int q,v,s,o;
    struct Node{int x,y; bool t; Node(int _x,int _y,bool _t):x(_x),y(_y),t(_t){}};
    class Bag{
    	public:
    	int t,l,r,X[MAXQ],Y[MAXQ]; bool F[MAXQ];
    	int W[MAXM][MAXN],M[MAXM][MAXN];
    	void iit(bool f){l=0,r=2*s-1;}
    	void add(Node e){
    		++t; int x=X[t]=e.x,y=Y[t]=e.y; bool f=F[t]=e.t;
            if(t-1==r){	//t==r+1 -> t=r-s+1
                up(0,s-1,j) up(0,v,k) W[j][k]=W[j+s][k]; l+=s,r+=s;
            }
            up(0,v,j) W[t-l][j]=W[t-l-1][j];
            if(f) up(x,v,j) W[t-l][j]=max(W[t-l][j],W[t-l][j-x]+y);
            else  dn(v,x,j) W[t-l][j]=max(W[t-l][j],W[t-l][j-x]+y);
    		if(t%s==0) up(0,v,j) M[t/s][j]=W[t-l][j];
    	}
    	void ers(){
    		--t; if(t+1==l){
            	l-=s,r-=s; up(0,v,j) W[0][j]=M[l/s][j];
            	up(1,s-1,j){
                	int x=X[l+j],y=Y[l+j]; bool f=F[l+j];
                	up(0,v,k) W[j][k]=W[j-1][k];
                	if(f) up(x,v,k) W[j][k]=max(W[j][k],W[j][k-x]+y);
            		else  dn(v,x,k) W[j][k]=max(W[j][k],W[j][k-x]+y);
    			}
        	}
    	}
    	Node bnk(){return Node(X[t],Y[t],F[t]);}
    	int  val(int x){return W[t-l][x];}
    }B1,B2;
    int slv(int x){
    	int r=0; up(0,x,i) r=max(r,B1.val(i)+B2.val(x-i)); return r;
    }
    int main(){
    	q=qread(),v=qread(),s=1+sqrt(q+1)/2,B1.iit(1),B2.iit(0);
    	up(1,q,i){
        	i64 opt=qread()^o,ti=qread()^o,vi=qread()^o,wi=qread()^o,xi=qread()^o,yi=qread()^o;
        	switch(opt){
        		case 1: B1.add(B2.bnk()),B2.ers(); break;
        		case 2: B2.add(B1.bnk()),B1.ers(); break;
        		case 3: B2.add(Node(vi,wi,ti)); break; case 4: B2.ers(); break;
        		case 5: B2.ers(),B2.add(Node(vi,wi,ti));
    		}
    		printf("%d\n",o=xi+slv(yi));
    	}
        return 0;
    }
    
    • 1

    信息

    ID
    6571
    时间
    1500ms
    内存
    128MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者