Codeforces 1638 E. Colorful Operations —— 线段树+暴力,有丶东西

This way

题意:

给你一个长度为n的数组a,一开始所有位置的颜色都为1,值都为0.每次有三种操作:
Color l r x:把[l,r]中的所有位置颜色变成x
Add c:把颜色为c的所有位置的值加上c
Query i:问i位置的值是多少。

题解:

这个感觉还是有点绕的,我也是想了比较长的一段时间。最后在通过某位大佬的代码证实了我的想法之后才敲的,要不然我敲崩了又没有借鉴可以参考,非常难受。
首先这种区间变换的题目,很容易就能想到是区间修改,但是有的时候也不一定,所以我在决定做法的时候还是犹豫了一段时间的。
使用multiset维护每一段颜色。这样子的话Color l r x 的时候只需要把在l,r内部的删掉,然后两端的修改一下即可,这个时间复杂度应当是 O ( N l o g N ) O(NlogN) O(NlogN),因为你删掉的区间必然需要一次操作建立起来,所以复杂度可证不会很大。
然后呢?对于加值的问题,我们如果每次都把所有的对应颜色区间操作的话,时间复杂度就会很高了。所以此时应该是延时操作。
那么如何操作?
sum[i]表示从开始操作到现在,颜色为i的值之和是多少。
但是肯定出现一个情况就是说,后来新加入的颜色怎么办?
然后注意到我们新加入的颜色是一个区间的形式,那么我只要在这一段区间加入这个颜色的时候,区间修改这一段区间在线段树中的值是-sum[i]不就好了吗,然后sum和线段树对应的值一加,就相互抵消了。
然后颜色i变成颜色j的时候该怎么办,那么我们只需要在区间中加上sum[i]就可以了。和上面的操作连接起来,就变成了:
颜色i变成j的时候,首先加上sum[i]表示拿到之前的所有Add i操作,然后在减去sum[j]表示消去Add j操作的影响。这样子最终的值就是sum[j]+线段树单点查询的值。
最好还是看代码,一些变量解释:
st 表示存当前所有的颜色区间
col[i]表示以i开头的区间颜色
sum[i]表示从开头到现在,颜色i的值之和

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e6+5;
ll s[N*4],f[N*4];
void push_down(int root){
    if(!f[root])return ;
    s[root<<1]+=f[root];
    s[root<<1|1]+=f[root];
    f[root<<1]+=f[root];
    f[root<<1|1]+=f[root];
    f[root]=0;
}
void update(int l,int r,int root,int ql,int qr,ll v){
    if(l>=ql&&r<=qr){
        s[root]+=v;
        f[root]+=v;
        return ;
    }
    push_down(root);
    int mid=l+r>>1;
    if(mid>=ql)update(l,mid,root<<1,ql,qr,v);
    if(mid<qr)update(mid+1,r,root<<1|1,ql,qr,v);
    s[root]=s[root<<1]+s[root<<1|1];
}
ll query(int l,int r,int root,int p){
    if(l==r)return s[root];
    push_down(root);
    int mid=l+r>>1;
    if(mid>=p)return query(l,mid,root<<1,p);
    return query(mid+1,r,root<<1|1,p);
}
#define pii pair<int,int>
const int mx=1e9+7;
multiset<pii>st;
ll sum[N];
int col[N];
int main()
{
    char s[15];
    int n,m,l,r,v;
    scanf("%d%d",&n,&m);
    st.insert({0,0}),st.insert({mx,mx}),st.insert({1,n});
    col[1]=1;
    while(m--){
        scanf("%s",s);
        if(s[0]=='C'){
            scanf("%d%d%d",&l,&r,&v);
            multiset<pii>::iterator it=st.upper_bound({l,mx});it--;
            while(it->first<=r){
                auto [x,y]=*it;
                st.erase(it++);
                if(x<l)st.insert({x,l-1});
                if(y>r)st.insert({r+1,y}),col[r+1]=col[x];
                update(1,n,1,max(l,x),min(r,y),sum[col[x]]);
            }
            st.insert({l,r});
            col[l]=v;
            update(1,n,1,l,r,-sum[v]);
        }
        else if(s[0]=='A')
            scanf("%d%d",&l,&v),sum[l]+=v;
        else{
            scanf("%d",&l);
            multiset<pii>::iterator it=st.upper_bound({l,mx});
            it--;
            printf("%lld\n",query(1,n,1,l)+sum[col[it->first]]);
        }

    }
    return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值