CF 1638 E. Colorful Operations set 区间平推

文章目录

传送门

题意:

给你一个数组 a a a,初始价值全为 0 0 0,颜色全为 1 1 1,让后让你实现以下三个操作:

  1. [ l , r ] [l,r] [l,r]区间内的颜色都染成 c c c
  2. 将所有颜色为 c c c的位置价值都加上 x x x
  3. 询问 i i i位置的价值。

1 ≤ n , q ≤ 1 e 6 1\le n,q\le 1e6 1n,q1e6

思路:

首先分析一下第二个操作是全局的,这就提示我们每次执行第二个操作的时候可以打一个懒标记 l a z y [ c ] + = x lazy[c]+=x lazy[c]+=x,当询问某个点的价值的时候只需要输出 a i + l a z y [ c ] a_i+lazy[c] ai+lazy[c]即可, c c c为当前点的颜色。

但是这都是不考虑第一个操作的情况下的,考虑第一个操作,假设 l = = r l==r l==r,我们将他分为两个步骤:

  1. 首先将当前位置原本的颜色 c ′ c' c对应的 l a z y [ c ′ ] lazy[c'] lazy[c]加到 a l a_l al上。
  2. 将当前颜色改成 c c c

上述步骤很明显有一个问题,就是将修改前的 l a z y [ c ] lazy[c] lazy[c]的代价在第三个操作的时候也算进去了,这显然是不对的,不过我们发现可以在第二部中将 a l − l a z y [ c ] a_l-lazy[c] allazy[c],这样就可以消除上述多余的价值了。

我们解决了单个的,再来看区间的怎么搞。

直接暴力不可以,但是我们不难发现可以将其拆分成若干个区间,每个区间的颜色都相同,类似与珂朵莉树,保证都是推平操作的话区间个数下降的很快的,这样我们只需要用 s e t set set维护这些区间,让后快速找到并且可以遍历这些区间修改他们,让后将他们推平,这看似暴力的算法实际平摊下来是 O ( q ) O(q) O(q)的,由于对于每个区间我们还需要实现区间加某个数,显然 B I T BIT BIT就可以实现,总复杂度是 O ( q l o g n ) O(qlogn) O(qlogn)

#include<bits/stdc++.h>
#define Mid (tr[u].l+tr[u].r>>1)
#define pb push_back
#define IT set<node>::iterator
#define x first
#define y second
using namespace std;

const int N=2000010,INF=0x3f3f3f3f,mod=1e9+7;
typedef long long LL;
typedef pair<LL,LL> PII;

int n,m;
LL a[N];
vector<PII>v[N];

struct node
{
    int l,r,t;
    mutable int v;
    bool operator < (const node& o) const {
        return l<o.l;
    }
};
set<node>s;

int find(int col,int t) {
    int l=0,r=(int)v[col].size()-1,ans=-1;
    if(r==-1) return INF;
    while(l<=r) {
        int mid=l+r>>1;
        if(v[col][mid].y<t) ans=mid,l=mid+1;
        else r=mid-1;
    }
    return ans;
}

struct BIT {
    LL tr[N];
    #define lowbit(x) (x&(-x))
    void add(int x,LL c) {
        for(int i=x;i<N;i+=lowbit(i)) tr[i]+=c;
    }

    LL sum(int x) {
        LL ans=0;
        for(int i=x;i;i-=lowbit(i)) ans+=tr[i];
        return ans;
    }
}bit;

IT splitl(int pos) {
    IT it=s.upper_bound({pos,-1,-1,-1});
    --it;
    return it;
}

IT splitr(int pos) {
    IT it=s.upper_bound({pos,-1,-1,-1});
    return it;
}

void add(int l,int r,int c,int t) {
    IT itr=splitr(r+1);
    IT itl=splitl(l);
    for(;itl!=itr;++itl) {
        int ls=itl->l,rs=itl->r;
        ls=max(ls,l);
        rs=min(rs,r);
        int col=itl->v,t=itl->t;
        int pos=find(col,t);
        if(pos!=INF) {
            LL ed=v[col].back().x;
            if(pos!=-1) ed-=v[col][pos].x;
            bit.add(rs+1,-ed); bit.add(ls,ed);
        }
    }
    itl=splitl(l);
    node ls,rs;
    ls.l=-1; rs.l=-1;
    if(itl->l<l) {
        ls.l=itl->l; ls.r=l-1;
        ls.v=itl->v; ls.t=itl->t;
    }
    if(itl->r>r) {
        rs.l=r+1; rs.r=itl->r;
        rs.v=itl->v; rs.t=itl->t;
    }
    itr--;
    if(itr->r>r) {
        rs.l=r+1; rs.r=itr->r;
        rs.v=itr->v; rs.t=itr->t;
    }
    itr++;
    s.erase(itl,itr);
    if(ls.l!=-1) s.insert(ls);
    if(rs.l!=-1) s.insert(rs);
    s.insert({l,r,t,c});
}

LL query(int pos) {
    IT now=splitl(pos);
    int t=now->t,col=now->v;
    pos=find(col,t);
    LL ans=0;
    if(pos!=INF) {
        LL ed=v[col].back().x;
        if(pos!=-1) ed-=v[col][pos].x;
        ans+=ed;
    }
    return ans;
}

void solve() {
    scanf("%d%d",&n,&m);
    s.insert({1,n,0,1});
    for(int i=1;i<=m;i++) {
        char ss[10];
        scanf("%s",ss+1);
        if(ss[1]=='C') {
            int l,r,c; scanf("%d%d%d",&l,&r,&c);
            add(l,r,c,i);

        } else if(ss[1]=='A') {
            int c,x; scanf("%d%d",&c,&x);
            if(v[c].size()) {
                LL ed=v[c].back().x;
                v[c].pb({x+ed,i});
            }
            else v[c].pb({x,i});
        } else if(ss[1]=='Q') {
            int x; scanf("%d",&x);
            printf("%lld\n",bit.sum(x)+query(x));
        }
    }
}

int main() {
	int _=1;
	while(_--) {
		solve();
	}

    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值