Codeforces Round #312 E. A Simple Task (桶排序思想+线段树维护桶)

传送门

给定一个只包含小写字母的字符串,长度为n,
m次操作,每次指定 [L,R] K K=0,让[L,R]区间降序排列,否则升序排列。
输出最终字符串。

由于每个数都相当于是[0,25]区间内,我们试试能不能开26个桶,第i个桶记录有多少个i,利用桶排序来求解。

我们开n个桶,每个桶大小都是26,记录第i位的26个数分布情况。

线段树维护桶序列,每个节点表示:
当前区间内,桶合并后的情况,即:【0,25】这些数各有多少个。

于是每次操作就可以这样处理:

  • 先区间查询出当前询问区间[L,R]里面,【0,25】各有多少个,保存在cnt[i],假设本次是升序操作,那么就枚举0~25(降序就倒着枚举),一段一段地放到区间上去,即:区间覆盖,当前区间的桶,只剩下一种数,其余的全部清零。
  • 区间覆盖要用到懒标记,记录当前区间最后一次被哪个数覆盖。

最后的输出直接访问每个叶子节点即可。

#include<bits/stdc++.h>
using namespace std;
//#pragma GCC optimize(2)
#define ull unsigned long long
#define ll long long
#define pii pair<int, int>
const int maxn = 1e5 + 10;
const ll mod = 998244353;
const ll inf = (ll)4e16+5;
const int INF = 1e9 + 7;
const double pi = acos(-1.0);
ll inv(ll b){
    
    if(b==1)return 1;return(mod-mod/b)*inv(mod%b)%mod;}
inline ll read()
{
    
    
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
    
    if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){
    
    x=x*10+ch-'0';ch=getchar();}
    return x*f;
}

struct node 
{
    
    
    #define lc rt<<1
    #define rc rt<<1|1
    int l,r;
    int v[26];//维护桶
}tree[maxn<<2];
int tag[maxn<<2];//懒标记 rt区间最后一次被什么覆盖 0表示没有,1表示'a',etc..
char s[maxn];
inline void pushup(int rt)
{
    
    
    for(int i=0;i<26;i++) tree[rt].v[i]=tree[lc].v[i]+tree[rc].v[i];
}
void build(int rt,int l,int r)
{
    
    
    tree[rt].l=l;tree[rt].r=r;
    if(l==r)
    {
    
    
        int x=s[l]-'a';
        tree[rt].v[x]=1;
        return ;
    }
    int mid=l+r>>1;
    build(lc,l,mid);build(rc,mid+1,r);
    pushup(rt);
}
inline void change(int rt,int k) //改成第k种颜色
{
    
    
    for(int i=0;i<26;i++)
    {
    
    
        tree[rt].v[i]=0;//先全部清零,[l,r]区间被k覆盖 r-l+1个k
    }
    int l=tree[rt].l,r=tree[rt].r;
    tree[rt].v[k]=r-l+1,tag[rt]=k+1;//懒标记别忘了
}
inline void pushdown(int rt) 
{
    
    
    if(tag[rt]) 
    {
    
    
        change(lc,tag[rt]-1);
        change(rc,tag[rt]-1);
        tag[rt]=0;
    }
}
inline int qry(int rt,int vl,int vr,int k)
{
    
    
    int l=tree[rt].l,r=tree[rt].r;
    if(l>vr || r<vl) return 0;
    if(vl<=l && r<=vr) return tree[rt].v[k];
    pushdown(rt);
    return qry(lc,vl,vr,k)+qry(rc,vl,vr,k);
}
inline void upd(int rt,int vl,int vr,int k)//区间覆盖
{
    
    
    int l=tree[rt].l,r=tree[rt].r;
    if(l>vr || r<vl) return ;
    if(vl<=l && r<=vr) 
    {
    
    
        change(rt,k);//rt被k个数覆盖
        return ;
    }
    pushdown(rt);
    upd(lc,vl,vr,k);upd(rc,vl,vr,k);
    pushup(rt);
}
void output(int rt,int l,int r)
{
    
    
    if(l==r) 
    {
    
    
        for(int i=0;i<26;i++)
        {
    
    
            if(tree[rt].v[i])
            {
    
    
                printf("%c",'a'+i);
                return ;
            }      
        }
    }
    int mid=l+r>>1;
    pushdown(rt);
    output(lc,l,mid);
    output(rc,mid+1,r);
}
int n,m;
int main()
{
    
    
    scanf("%d %d %s",&n,&m,s+1);
    build(1,1,n);
    while(m--)
    {
    
    
        int l,r,f;
        scanf("%d %d %d",&l,&r,&f);
        int cnt[27];
        for(int i=0;i<26;i++) cnt[i]=qry(1,l,r,i);
        if(f) 
        {
    
    
            for(int i=0;i<26;i++)
            {
    
    
                if(!cnt[i]) continue;
                //[l,l+cnt[i]-1]这个区间中 只剩下i这个数了  去更新区间对应的桶
                upd(1,l,l+cnt[i]-1,i);
                l=l+cnt[i];
            }
        }
        else 
        {
    
    
            for(int i=25;i>=0;i--)
            {
    
    
                if(!cnt[i]) continue;
                upd(1,l,l+cnt[i]-1,i);
                l=l+cnt[i];
            }
        } 
    }
    output(1,1,n);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_46030630/article/details/121149157