Codeforces 558E 线段树处理字符串内排序

给出长度为n的字符串,m个操作。

每一个操作有三个值 l,r,op。

op==1,表示将字符串中[ l ,r ]的部分依照升序排列。

op==0,表示将字符串中[ l ,r ]的部分依照降序排列。

输出终于的字符串

按小写字母建26颗线段树

对于每次改动,先记录[l,r]区间内各个字母出现的次数,并对对应区间清空,然后依照升序或者降序从新更新

#include "stdio.h"
#include "string.h"

char str[100010];
int n,temp;
struct node
{
    int l,r,x,lazy;
}data[30][400010];
void build(int l,int r,int k,int op)
{
    int mid;
    data[op][k].l=l;
    data[op][k].r=r;
    data[op][k].x=0;
    data[op][k].lazy=-1;
    if (l==r)
        return ;

    mid=(l+r)/2;
    build(l,mid,k*2,op);
    build(mid+1,r,k*2+1,op);
}

void Pushdown(int k,int op)
{
    if (data[op][k].lazy==-1) return ;
    if (data[op][k].l==data[op][k].r) return ;

    if (data[op][k].lazy==0)
    {
        data[op][k*2].x=data[op][k*2].lazy=0;
        data[op][k*2+1].x=data[op][k*2+1].lazy=0;
    }
    else
    {
        data[op][k*2].x=data[op][k*2].r-data[op][k*2].l+1;
        data[op][k*2+1].x=data[op][k*2+1].r-data[op][k*2+1].l+1;
        data[op][k*2].lazy=data[op][k*2+1].lazy=1;
    }
    data[op][k].lazy=-1;

}

void updata(int l,int r,int k,int op)
{
    int mid;
    if (data[op][k].l==l && data[op][k].r==r)
    {
        data[op][k].x=data[op][k].r-data[op][k].l+1;
        data[op][k].lazy=1;
        return;
    }

    Pushdown(k,op);

    mid=(data[op][k].l+data[op][k].r)/2;

    if (r<=mid) updata(l,r,k*2,op);
    else
        if (l>mid) updata(l,r,k*2+1,op);
    else
    {
        updata(l,mid,k*2,op);
        updata(mid+1,r,k*2+1,op);
    }

    data[op][k].x=data[op][k*2].x+data[op][k*2+1].x;

}

void  search(int l,int r,int k,int op)
{
    int mid;
    if (data[op][k].l==l && data[op][k].r==r)
    {
        temp+=data[op][k].x;
        data[op][k].x=0;
        data[op][k].lazy=0;
        return ;
    }

    Pushdown(k,op);

    mid=(data[op][k].l+data[op][k].r)/2;

    if (r<=mid) search(l,r,k*2,op);
    else if (l>mid)  search(l,r,k*2+1,op);
    else
    {
        search(l,mid,k*2,op);
        search(mid+1,r,k*2+1,op);
    }

    data[op][k].x=data[op][k*2].x+data[op][k*2+1].x;
}
void init()
{
    int i;
    scanf("%s",str);
    for (i=0;i<26;i++)
        build(1,n,1,i);
    for (i=0;i<n;i++)
        updata(i+1,i+1,1,str[i]-‘a‘);
}
int main()
{
    int m,a,b,c,i,j,k;
    int mark[30];
    while (scanf("%d%d",&n,&m)!=EOF)
    {
        init();

        while (m--)
        {
            scanf("%d%d%d",&a,&b,&c);
            memset(mark,0,sizeof(mark));
            for (i=0;i<26;i++)
            {
                temp=0;
                search(a,b,1,i); // 查找区间内i字母出现的次数,并清空
                mark[i]+=temp;
            }
            if (c==0)
            {
                k=a;
                for (i=25;i>=0;i--)
                    if (mark[i]!=0)
                    {
                        updata(k,k+mark[i]-1,1,i); // 更新区间字母
                        k+=mark[i];
                    }
            }
            else
            {
                k=a;
                for (i=0;i<26;i++)
                    if (mark[i]!=0)
                    {
                        updata(k,k+mark[i]-1,1,i);
                        k+=mark[i];

                    }
            }
        }
        for (i=1;i<=n;i++)
        {
            for (j=0;j<26;j++)
            {
                temp=0;
                search(i,i,1,j);
                if (temp!=0)
                {
                    printf("%c",j+‘a‘);
                    break;
                }
            }
        }
        printf("\n");
    }
    return 0;
}
时间: 2024-12-09 20:13:05

Codeforces 558E 线段树处理字符串内排序的相关文章

Codeforces 384E 线段树+dfs序

题目链接:点击打开链接 题意: 给定n个点,m个询问的无向树(1为根) 下面n个数表示每个点的权值 下面n-1行给出树 操作1:x点权值+v, x的第 i & 1 的儿子-v, 第 !(i&1) 的儿子+v 操作2:询问x点权值 dfs把树转成序列 根据深度把点分成2组 分别用线段树维护.. 然后Y一下 #include<stdio.h> #include<string.h> #include<iostream> #include<algorith

Codeforces 446C 线段树 递推Fibonacci公式

聪哥推荐的题目 区间修改和区间查询,但是此题新颖之处就在于他的区间修改不是个定值,而是从L 到 R 分别加 F1.F2....Fr-l+1 (F为斐波那契数列) 想了一下之后,觉得用fib的前缀和来解决,每次做懒惰标记记录下当前区间是从哪个L开始加起的,敲了一半之后发现有问题,就跟上次遇到的懒惰标记问题一样,这是个覆盖性的懒惰标记,每次向下传递后,都要先清除孩子的,清除孩子的也有可能要清除son's son,所以要一直pushdown下去,否则就会错,但这样就会超时. 能不能有个累加型的标记让我

Bash and a Tough Math Puzzle CodeForces 914D 线段树+gcd数论

Bash and a Tough Math Puzzle CodeForces 914D 线段树+gcd数论 题意 给你一段数,然后小明去猜某一区间内的gcd,这里不一定是准确值,如果在这个区间内改变一个数的值(注意不是真的改变),使得这个区间的gcd是小明所猜的数也算小明猜对.另一种操作就是真的修改某一点的值. 解题思路 这里我们使用线段树,维护区间内的gcd,判断的时候需要判断这个区间的左右子区间的gcd是不是小明猜的数的倍数或者就是小明猜的数,如果是,那么小明猜对了.否则就需要进入这个区间

Codeforces 833B 线段树优化 dp

Codeforces  833B  The Bakery 题意: n 个数要分成 k 块,每块的价值是其不同数的个数,问价值和最大是多少. tags: dp[i][j]表示前 j 个数分成 i 块的最大权值和,转移: dp[i][j] = max( dp[i-1][k] + val[k+1][j] ) , k是 1~j . 但这个过程其实并不好转移,要利用累加的特点,用线段树进行优化 (感觉我不看题解是想不到的,2333) 大概就是,对于第 i 层,我们假定已经知道了第 i-1 层,也就是求出了

Codeforces 938G 线段树分治 线性基 可撤销并查集

Codeforces 938G Shortest Path Queries 一张连通图,三种操作 1.给x和y之间加上边权为d的边,保证不会产生重边 2.删除x和y之间的边,保证此边之前存在 3.询问x到y的路径异或最小值 保证图在任意时刻连通 首先连通图路径异或相当于从x到y的任意一条路径再异或上若干个环得到的,只要在dfs过程中把非树边成的环丢到线性基里就好了,其他环一定可以通过这些环异或组合出来 有加边删边操作怎么做呢?线段树时间分治!注意到不能保证在线段树的任意一个节点图是连通的,需要用

codeforces 589G:线段树+二分

离线做 先按照t[]建线段树,维护区间的有效天数以及可用时间 然后把t[]从小到达排序,记得记录t中元素在线段树中的位置 把询问按照d从小到大排序,依次考虑 由于按照d排序,所以当前询问肯定是d最短的,我们在t数组中找到大于当前d的第一个元素位置,把这之前的元素全部从线段树中删除,表示这些天数无效 因为当前d已经是最小,所以删除对后续不会有影响 二分一个天数,查找0~mid天一共的工作时间(工作时间=总可用时间-准备时间*有效天数) #include"cstdio" #include&

CodeForces 343D 线段树维护dfs序

给定一棵树,初始时树为空 操作1,往某个结点注水,那么该结点的子树都注满了水 操作2,将某个结点的水放空,那么该结点的父亲的水也就放空了 操作3,询问某个点是否有水 我们将树进行dfs, 生成in[u], 访问结点u的时间戳,out[u],离开结点u的时间戳 每个结点的in值对应在线段树中的区间的一点 那么对于操作1, 只要将区间[in[u],out[u]] 的值都改为1, 但是如果区间[in[u],out[u]] 原先存在为0的点,那么父区间肯定是空的,这个操作不能 改变父区间的状态,所以需要

codeforces 997E(线段树)

分析: 首先考虑如何计算整个数组有多少个good区间 容易发现一个区间是good区间当且仅当max-min-len=-1,且任意区间max-min-len>=-1 我们可以枚举右端点,然后维护前面每个位置到当前右端点的max-min-len值,容易发现我们只需要维护区间最小值和最小值的个数就行了,于是用线段树即可 于是我们可以得到以某个点为右端点的时候合法区间总数,那么我们把每次的结果加起来,就得到了整个数组有多少个good区间 每次右端点移动怎么更新呢?显然我们只需要用一个max单调栈,一个m

Blog Post Rating CodeForces - 806E (线段树二分)

题目链接 题目大意: 有一个博客, 初始分数为$0$, 有$n$个人, 第$i$个人有一个期望值$a_i$, 如果第$i$个人浏览博客时,博客赞数高于$a_i$博客分数$-1$, 低于$+1$, 相等不变, 对于每个$i$, 求出$[1,i]$的人按任意顺序浏览博客后最大分数. 题解: 首先, 用贪心可以知道所有人按期望升序排列, 最后得分一定最大 由于期望有负数, 博客分数一定是先减后增的, 然后对这两段分类讨论 对于递减的段, 最后分数为递增递减的临界值 假设临界值为$x$, 设比$x$小的