CF911G Mass Change Queries (线段树区间 合并)

本文介绍了一种使用线段树解决特定数列更新问题的方法。通过动态为每种颜色开辟线段树并实现区间合并,高效处理区间内元素值的批量更改,最后输出修改后的数列。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意:
给出一个数列,有q个操作,每种操作是把区间[l,r]中等于x的数改成y.输出q步操作完的数列.
题解:
100个数,很容易想到要从这里进行突破,对于某次操作我们只需要把这个区间的数x给移动到y的这个数的集合里面不就可以了。线段树合并!
这样的话我们对于每一种颜色动态开一个线段树,合并就把颜色为x的线段树区间l r合并到颜色为y的线段树的区间l r 上即可。
之前大多数的线段树合并是整颗线段树进行合并,这个是针对于某段区间进行合并,我们需要稍微调整一下这个update即可。
最后只需要遍历一遍这100棵线段树然后把有值的位置存到ans数组里面最后输出即可。
代码:

//#pragma GCC optimize(3)
#include <bits/stdc++.h>
using namespace std;
//#define int long long
const int maxn=2e7+10;
#define endl '\n'


int rt[110];
int ls[maxn],rs[maxn];
int cnt;
//bool tree[maxn];

void ins(int &node,int start,int ends,int pos){
    if(!node) node=++cnt;
    if(start==ends){
        //tree[node]=true;
        return ;
    }
    int mid=(start+ends)/2;
    if(pos<=mid) ins(ls[node],start,mid,pos);
    else ins(rs[node],mid+1,ends,pos);
    //push(node);
}

int mer(int x,int y){
    if(!x||!y) return x+y;
    ls[x]=mer(ls[x],ls[y]);
    rs[x]=mer(rs[x],rs[y]);
    return x;
}

void update(int &x,int &y,int start,int ends,int l,int r){  //y合并到x
    if(!y) return ;
    if(l<=start&&ends<=r){
        x=mer(x,y);y=0;
        return ;
    }
    if(!x) x=++cnt;   //x如果没有需要开辟新的结点
    int mid=(start+ends)/2;
    if(l<=mid) update(ls[x],ls[y],start,mid,l,r);
    if(mid<r) update(rs[x],rs[y],mid+1,ends,l,r);


}
int ans[200010];
void query(int node,int start,int ends,int val){
    if(node==0) return ;
    if(start==ends){
        ans[start]=val;
        return ;
    }
    int mid=(start+ends)/2;
    query(ls[node],start,mid,val);
    query(rs[node],mid+1,ends,val);
}

signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        ins(rt[x],1,200000,i);
    }
    int q;
    cin>>q;
    while(q--){
        int l,r,x,y;
        cin>>l>>r>>x>>y;
        if(x==y) continue;
        update(rt[y],rt[x],1,200000,l,r);
    }
    for(int i=1;i<=100;i++){
        query(rt[i],1,200000,i);
    }
    for(int i=1;i<=n;i++) cout<<ans[i]<<" ";



}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值