CF558E A Simple Task 线段树

  

题意翻译

题目大意: 给定一个长度不超过10^5的字符串(小写英文字母),和不超过50000个操作。

每个操作 L R K 表示给区间[L,R]的字符串排序,K=1为升序,K=0为降序。

最后输出最终的字符串。

给每个数字开一个线段树    如果该数字在pos位置  那么在该数字的线段树的pos 位置+1  (有一点类似权值线段树)

因为一个点不是0就是1  (不可能有两个数字在一个点)开绝对标记即可

每次操作的时候  如果是递增的从小到大遍历数字的线段树即可  每次先找出区间个数  放在最左边即可

更改的代码非常秒  见代码

#include<bits/stdc++.h>
using namespace std;
//input by bxd
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define repp(i,a,b) for(int i=(a);i>=(b);--i)
#define RI(n) scanf("%d",&(n))
#define RII(n,m) scanf("%d%d",&n,&m)
#define RIII(n,m,k) scanf("%d%d%d",&n,&m,&k)
#define RS(s) scanf("%s",s)
#define ll long long
#define see(x) (cerr<<(#x)<<‘=‘<<(x)<<endl)
#define pb push_back
#define inf 0x3f3f3f3f
#define CLR(A,v)  memset(A,v,sizeof A)
#define lson l,m,pos<<1
#define rson m+1,r,pos<<1|1
typedef pair<int,int>pii;
//////////////////////////////////
const int N=1e5+10;

int t[30][N<<2],col[30][N<<2],x,y,k,n,m,pos,cnt;
char s[N],ans[N];

void up(int pos,int num)
{
    t[num][pos]=t[num][pos<<1]+t[num][pos<<1|1];
}
void build(int l,int r,int pos)
{
    if(l==r){t[s[l]-‘a‘+1][pos]=1;return;}
    int m=(l+r)>>1;build(lson);build(rson);rep(i,1,26)up(pos,i);
}
void down(int pos,int num,int m)
{
    if(col[num][pos]==-1)
    {
        t[num][pos<<1]=t[num][pos<<1|1]=0;
        col[num][pos<<1]=col[num][pos<<1|1]=-1;
        col[num][pos]=0;
    }
    else if(col[num][pos]>=1)
    {
        t[num][pos<<1]=(m-(m>>1));
        t[num][pos<<1|1]=(m>>1);
        col[num][pos<<1]=col[num][pos<<1|1]=1;
        col[num][pos]=0;
    }
}
int qsum(int L,int R,int num,int l,int r,int pos)
{
    int ans=0;if(L<=l&&r<=R)return t[num][pos];
    int m=(l+r)>>1;down(pos,num,r-l+1);
    if(L<=m)ans+=qsum(L,R,num,lson);if(R>m)ans+=qsum(L,R,num,rson);
    return ans;
}

void upsum(int L,int R,int num,int v,int l,int r,int pos)
{
    if(L<=l&&r<=R){  if(v){ col[num][pos]=1;t[num][pos]=r-l+1; return ; } else { col[num][pos]=-1;t[num][pos]=0; return; }    }//注意一开始写成col[num][pos]++ wa 烂了 -1会加到0
    int m=(l+r)>>1;down(pos,num,r-l+1);
    if(L<=m)upsum(L,R,num,v,lson);if(R>m)upsum(L,R,num,v,rson);
    up(pos,num);
}
void seeall(int l,int r,int pos)
{
    if(l==r)
    {
        rep(i,1,26)
        if(t[i][pos]){ans[l]=i+‘a‘-1;  break; }
        return ;
    }int m=(l+r)>>1;
    rep(i,1,26)down(pos,i,r-l+1);
    seeall(lson);seeall(rson);
}
int main()
{
    RII(n,m);
    RS(s+1);
    build(1,n,1);

    while(m--)
    {
        RIII(x,y,k);pos=x;
        if(k)
        {
            rep(i,1,26)
            {
                cnt=qsum(x,y,i,1,n,1);
                if(!cnt)continue;
                upsum(x,y,i,0,1,n,1);
                upsum(pos,pos+cnt-1,i,1,1,n,1);pos=pos+cnt;
            }
        }
        else
        {
            repp(i,26,1)
            {
                cnt=qsum(x,y,i,1,n,1);
                if(!cnt)continue;
                upsum(x,y,i,0,1,n,1);
                upsum(pos,pos+cnt-1,i,1,1,n,1);pos=pos+cnt;
            }
        }
    }
    seeall(1,n,1);
    printf("%s",ans+1);
    return 0;
}

原文地址:https://www.cnblogs.com/bxd123/p/11185240.html

时间: 2024-08-27 06:43:39

CF558E A Simple Task 线段树的相关文章

CF558E - A Simple Task线段树

给出一个小写字母组成的字符串,然后q个操作,a,b,c ,c为0 ,将区间 [a,b] 按逆字典序排,c为1,将此区间按字典序排.用一颗线段树,维护每个节点各个字母出现的种类数,每次操作操作后,暴力将字母a组成的区间,字母b组成的区间等等,区间内所有元素更新为a,b,等等.打个lazy标记,区间更新搞下就行了. #pragma comment(linker,"/STACK:102400000,102400000") #define _CRT_SECURE_NO_WARNINGS #in

codeforces 558E A Simple Task 线段树

题目链接 题意较为简单. 思路: 由于仅仅有26个字母,所以用26棵线段树维护就好了,比較easy. #include <iostream> #include <string> #include <vector> #include <cstring> #include <cstdio> #include <map> #include <queue> #include <algorithm> #include &

HDU 3974 Assign the task(线段树)

描述There is a company that has N employees(numbered from 1 to N),every employee in the company has a immediate boss (except for the leader of whole company).If you are the immediate boss of someone,that person is your subordinate, and all his subordin

CF558E A Simple Task

题目大意: 给定一个长度不超过10^5的字符串(小写英文字母),和不超过5000个操作. 每个操作 L R K 表示给区间[L,R]的字符串排序,K=1为升序,K=0为降序. 最后输出最终的字符串 首先这么想想,对于一段区间的排序,排完序的样子和排序之前每个字母的位置并没有关系,而是和每一个字母出现的次数有关.所以我们对于每一次操作,统计出区间中每一个字母出现了多少次,然后按字典序排序就行.更确切的说,就是这个区间中的哪一个部分都改成某一个字母,区间修改. 既然是区间修改,那么就可以用线段树实现

hdu 3974 Assign the task 线段树 DFS序

给你一棵树,每次修改一个子树的所有值,然后单点查询. 按照DFS序把节点排列(即在DFS中出现的先后次序),同一个子树在序列中连续. 1 #include <cstdio> 2 using namespace std; 3 typedef long long ll; 4 int n,q,T,Tc,cnt,sum; 5 int col[210000],lzy[210000],sta[51000],fin[51000]; 6 int nxt[51000],to[51000],head[51000]

计数排序 + 线段树优化 --- Codeforces 558E : A Simple Task

E. A Simple Task Problem's Link: http://codeforces.com/problemset/problem/558/E Mean: 给定一个字符串,有q次操作,每次操作将(l,r)内的字符升序或降序排列,输出q次操作后的字符串. analyse: 基本思想是计数排序. 所谓计数排序,是对一个元素分布较集中的数字集群进行排序的算法,时间复杂度为O(n),但使用条件很苛刻.首先对n个数扫一遍,映射出每个数字出现的次数,然后再O(n)扫一遍处理出:对于数字ai,

Codeforces 558E A Simple Task (计数排序&amp;&amp;线段树优化)

题目链接:http://codeforces.com/contest/558/problem/E E. A Simple Task time limit per test5 seconds memory limit per test512 megabytes inputstandard input outputstandard output This task is very simple. Given a string S of length n and q queries each quer

[线段树] codeforces 558E. A Simple Task

题意: 给一个长度n的字符串,q次操作,每次操作把[l,r]排序,k=0非递增,k=1非递减. 题解: 采用计数排序的复杂度是O(n?q),无法通过,但有所启示. 可以看出计数就是区间求和,排序就是区间更新,可以用线段树维护. 做法是建立26棵线段树,第i棵树维护第i个字母的位置信息. 计数时,在26棵线段树内分别做一次查询,排序时根据递增还是递减,把相应的区间赋值为相应的字母. #include<bits/stdc++.h> #define lson rt<<1,l,mid #d

CF 558E(A Simple Task-计数排序+线段树)

E. A Simple Task time limit per test 5 seconds memory limit per test 512 megabytes input standard input output standard output This task is very simple. Given a string S of length n and q queries each query is on the format i j k which means sort the