1 条题解

  • 0
    @ 2025-8-24 23:07:08

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar ARIS2_0
    路虽远,行则将至;事虽难,做则必成。

    搬运于2025-08-24 23:07:08,当前版本为作者最后更新于2024-12-16 12:42:15,作者可能在搬运后再次修改,您可在原文处查看最新版

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

    以下是正文


    前言

    好神奇的题面,赛时看了半天才看懂。

    简化题意

    f(x)f(x) 为拥有 xx 块磁铁时,最大的能够激活的磁铁数量。

    则本题题意即为:

    给定长度为 nn 的数组 aa,第一种操作将区间 [l,r][l,r] 加上 xx,第二种操作询问 i=lrf(2×i)\sum\limits_{i=l}^rf(2\times i)

    解题思路

    考虑计算 f(2×i)f(2\times i)。由于要求有一个 y[1,i]y\in[1,i] 使得没有任意两个激活的磁铁的距离为 yy,不难发现,对于一个固定的 yy,激活磁铁的最优策略一定是激活 yy 个,不激活 yy 个,再激活 yy 个,以此类推,直到第 2×i2\times i 个磁铁。

    考虑把磁铁的激活构造为如下形式:设 pos=2×i3pos=\lceil\frac{2\times i}{3}\rceil,我们采用前面激活 pospos 个,中间不激活 pospos 个,最后激活 2×i2×pos2\times i-2\times pos 个,总激活个数为 $2\times i-pos=2\times i-\lceil\frac{2\times i}{3}\rceil=\lfloor\frac{4\times i}{3}\rfloor=i+\lfloor\frac{i}{3}\rfloor$。

    以下为证明:

    • pos=2×i3+1pos=\lceil\frac{2\times i}{3}\rceil+1,前面激活和中间不激活的个数各 +1+1,后面激活的个数不多只少,不优。

    • pos=2×i31pos=\lceil\frac{2\times i}{3}\rceil-1,前面激活和中间不激活的个数各 1-1,而因为 3×pos<2×i3\times pos<2\times i,所以会有不激活的“第四部分”占据末尾,此时最大激活个数为 $2\times pos=2\times(\lceil\frac{2\times i}{3}\rceil-1)\le2\times\lfloor\frac{2\times i}{3}\rfloor\le\lfloor\frac{4\times i}{3}\rfloor$,同样不优。

    这样,我们不是很严谨地证明了 f(2×i)=i+i3f(2\times i)=i+\lfloor\frac{i}{3}\rfloor

    欢迎提供更严谨的证明,本蒟蒻还是太菜了。

    代码实现

    下文称 $ \sum\limits_{i=l}^rf(2\times i)=\sum\limits_{i=l}^ri\times\lfloor\frac{i}{3}\rfloor$ 为区间 [l,r][l,r] 的权值

    使用线段树,维护每个区间的权值、模 330,1,20,1,2 的数的个数,分别记为 wi,resti,0,resti,1,resti,2w_i,rest_{i,0},rest_{i,1},rest_{i,2}

    考虑如何实现区间加操作:假设现在要把编号为 ii ,长度为 lenlen 的区间加上 pospos

    1. posmod3=0pos \bmod 3=0:$w_i+(pos+\lfloor\frac{pos}{3}\rfloor)\times len\to w_i$。

    2. posmod3=1pos \bmod 3=1:$w_i+(pos+\lfloor\frac{pos}{3}\rfloor)\times len+rest_{i,2}\to w_i$,然后使 resti,0,resti,1,resti,2rest_{i,0},rest_{i,1},rest_{i,2} 变为 resti,1,resti,2,resti,0rest_{i,1},rest_{i,2},rest_{i,0}

    3. posmod3=2pos \bmod 3=2:$w_i+(pos+\lfloor\frac{pos}{3}\rfloor)\times len+rest_{i,1}+rest_{i,2}\to w_i$,然后使 resti,0,resti,1,resti,2rest_{i,0},rest_{i,1},rest_{i,2} 变为 resti,2,resti,0,resti,1rest_{i,2},rest_{i,0},rest_{i,1}

    于是,我们可以写出以下的 maketag()\rm{maketag()} 函数:

    void maketag(int id,int pos,int len){
        tag[id]+=pos;
        switch(pos%3){
            case 0:{
                w[id]+=(pos+pos/3)*len;
                break;
            }
            case 1:{
                w[id]+=(pos+pos/3)*len+rest[id][2];
                int res=rest[id][2];
                rest[id][2]=rest[id][1];
                rest[id][1]=rest[id][0];
                rest[id][0]=res;
                break;
            }
            case 2:{
                w[id]+=(pos+pos/3)*len+rest[id][1]+rest[id][2];
                int res=rest[id][2];
                rest[id][2]=rest[id][0];
                rest[id][0]=rest[id][1];
                rest[id][1]=res;
                break;
            }
        }
    }
    

    但考虑到 pospos 可能为负数,这样写连样例都过不去。

    不过,我们可以找到一个最大的 pposppos 满足 ppospos,pposmod3=0ppos\le pos,ppos \bmod 3=0,然后先调用 maketag(id,ppos,len)\rm{maketag}(\textit{id,ppos,len}),再调用 maketag(id,pos-ppos,len)\rm{maketag}(\textit{id,pos-ppos,len}),这样可以保证正确性而且不用特判负数。

    于是这道题就做完了。

    AC Code

    #include<bits/stdc++.h>
    using namespace std;
    #define int long long
    #define pii pair<int,int>
    #define fi first
    #define se second
    const int inf=1e16,maxn=5e5+10;
    int a[maxn],w[4*maxn],rest[4*maxn][3],tag[4*maxn];
    void pushup(int id){
        w[id]=w[id*2]+w[id*2+1];
        for(int i:{0,1,2})rest[id][i]=rest[id*2][i]+rest[id*2+1][i];
    }
    void build(int id,int l,int r){
        if(l==r){w[id]=a[l]+a[l]/3;rest[id][a[l]%3]=1;return;}
        int mid=(l+r)/2;
        build(id*2,l,mid);
        build(id*2+1,mid+1,r);
        pushup(id);
    }
    void maketag(int id,int pos,int len){
        if(pos<0 and pos%3){
            int ppos=pos;
            while(ppos%3)ppos--;
            maketag(id,ppos,len);
            maketag(id,pos-ppos,len);
            return;
        }
        tag[id]+=pos;
        switch(pos%3){
            case 0:{
                w[id]+=(pos+pos/3)*len;
                break;
            }
            case 1:{
                w[id]+=(pos+pos/3)*len+rest[id][2];
                int res=rest[id][2];
                rest[id][2]=rest[id][1];
                rest[id][1]=rest[id][0];
                rest[id][0]=res;
                break;
            }
            case 2:{
                w[id]+=(pos+pos/3)*len+rest[id][1]+rest[id][2];
                int res=rest[id][2];
                rest[id][2]=rest[id][0];
                rest[id][0]=rest[id][1];
                rest[id][1]=res;
                break;
            }
        }
    }
    void pushdown(int id,int l,int r){
        int mid=(l+r)/2;
        maketag(id*2,tag[id],mid-l+1);
        maketag(id*2+1,tag[id],r-mid);
        tag[id]=0;
    }
    bool inr(int l,int r,int cl,int cr){return cl<=l and r<=cr;}
    bool outr(int l,int r,int cl,int cr){return cr<l or r<cl;}
    void update(int id,int l,int r,int cl,int cr,int pos){
        if(inr(l,r,cl,cr)){maketag(id,pos,r-l+1);return;}
        if(outr(l,r,cl,cr))return;
        pushdown(id,l,r);
        int mid=(l+r)/2;
        update(id*2,l,mid,cl,cr,pos);
        update(id*2+1,mid+1,r,cl,cr,pos);
        pushup(id);
    }
    int check(int id,int l,int r,int cl,int cr){
        if(inr(l,r,cl,cr))return w[id];
        if(outr(l,r,cl,cr))return 0;
        pushdown(id,l,r);
        int mid=(l+r)/2;
        return check(id*2,l,mid,cl,cr)+check(id*2+1,mid+1,r,cl,cr);
    }
    signed main(){
    	ios::sync_with_stdio(0);
    	cin.tie(0);
    	int n,m;cin>>n>>m;
    	for(int i=1;i<=n;i++)cin>>a[i];
    	build(1,1,n);
    	while(m--){
            int op,l,r;cin>>op>>l>>r;
            if(op==1){
                int x;cin>>x;
                update(1,1,n,l,r,x);
            }
            else cout<<check(1,1,n,l,r)<<"\n";
        }
    	return 0;
    }
    

    后言

    机房有同学通过找规律轻轻松松飚过 C 题和 E 题,我拼尽全力无法战胜。

    果然还是技不如人吗。

    • 1

    信息

    ID
    10889
    时间
    2000ms
    内存
    512MiB
    难度
    4
    标签
    递交数
    0
    已通过
    0
    上传者