首页 > 其他分享 >线段树维护区间字符的两道例题(CF240F CF558E)

线段树维护区间字符的两道例题(CF240F CF558E)

时间:2024-05-25 15:11:13浏览次数:24  
标签:rt CF240F int rson CF558E seg tag lson 例题

CF240F:https://www.luogu.com.cn/problem/CF240F

题目大意:给定一个长为n的由a到z组成的字符串,有m次操作,每次操作将[l,r]的字符串进行重排,得到字典序最小的字符串,输出m次操作后的字符串。

大致思路:1.首先我们要想区间内的字典序最小的回文串要怎么构造。 回文串无非就两种类型:有一个字母出现奇数次其余字母出现偶数次和所有字母都出现偶数次两种类型。那么当给出一段区间的时候我们要对这两类进行分类讨论,那么自然而然我们需要一种方法去快速的维护和查询区间内每个字母的数量。因为要对区间进行重组,所以区间内字符串原本是以什么样的顺序排列对重组过程其实没有一点影响。因此我们可以开26颗线段树,去维护每个字母。

2.在分类讨论的过程中,遇到两个及以上的字母出现奇数次那么就是无法操作,直接跳过即可。如果遇到一个出现奇数次的字符,那么回文串的中心位置一定是他,我们先填上即可。接着我们从大到小或者从小到大遍历26个字母都行,如果说在区间内存在这种字母那么以对称的方式在两边填充即可,这样能保证得到的回文串一定是字典序最小的。如果遇到所有字母都出现偶数次的情况那么直接填充,那么只要跳过中心填充出现奇数次的字母那一步即可。

3.至于填充的具体操作。可以先把原先的位置全部都初始化,然后再每次根据当前遍历到的字母出现次数固定一个长度区间,在左右两边进行填充即可。

代码:

#define maxn 100010
int n,m;
char s[maxn];
struct node{
    int l,r;
    int sum,tag;
}seg[26][maxn<<2];
void pushup(int c,int rt)
{
    seg[c][rt].sum=seg[c][lson].sum+seg[c][rson].sum;
}
void build(int c,int rt,int l,int r)
{
    seg[c][rt].l=l;
    seg[c][rt].r=r;
    seg[c][rt].tag=-1;
    if(l==r)
    {
        seg[c][rt].sum=((s[l]-'a')==c);
        return ;
    }
    int mid = (l+r)>>1;
    build(c,lson,l,mid);
    build(c,rson,mid+1,r);
    pushup(c,rt);
}
void pushdown(int c,int rt)
{
    if(seg[c][rt].tag!=-1)
    {
        seg[c][lson].sum=(seg[c][lson].r-seg[c][lson].l+1)*seg[c][rt].tag;
        seg[c][rson].sum=(seg[c][rson].r-seg[c][rson].l+1)*seg[c][rt].tag;
        seg[c][lson].tag=seg[c][rt].tag;
        seg[c][rson].tag=seg[c][rt].tag;
        seg[c][rt].tag=-1;
    }
}
void modify(int c,int rt,int x,int y,int val)
{
    int l=seg[c][rt].l;
    int r=seg[c][rt].r;
    if(x<=l&&r<=y)
    {
        seg[c][rt].sum=(r-l+1)*val;
        seg[c][rt].tag=val;
        return ;
    }
    int mid = (l+r)>>1;
    pushdown(c,rt);
    if(x<=mid) modify(c,lson,x,y,val);
    if(y>mid) modify(c,rson,x,y,val);
    pushup(c,rt);
}
int query(int c,int rt,int x,int y)
{
    int l=seg[c][rt].l;
    int r=seg[c][rt].r;
    if(x<=l&&r<=y)
    {
        return seg[c][rt].sum;
    }
    int mid = (l+r)>>1;
    pushdown(c,rt);
    int ans=0;
    if(x<=mid) ans+=query(c,lson,x,y);
    if(y>mid) ans+=query(c,rson,x,y);
    return ans;
}
int main()
{
    #ifdef ONLINE_JUDGE
    freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);
    #endif
    // ios::sync_with_stdio(false);
    // cin.tie(0);
    // cout.tie(0);
    scanf("%d%d%s",&n,&m,s+1);
    for(int i=0;i<26;i++)
    {
        build(i,1,1,n);
    }
    while(m--)
    {
        int l,r;
        scanf("%d%d",&l,&r);
        int a[26];
        int odd=0;
        int le=-1;
        for(int i=0;i<26;i++)
        {
            a[i]=query(i,1,l,r);
            if(a[i]&1)
            {
                ++odd;
                le=i;
            }
        }
        if(odd>1) continue;
        for(int i=0;i<26;i++)
        {
            modify(i,1,l,r,0);
        }
        if(odd)
        {
            --a[le];
            modify(le,1,(l+r)/2,(l+r)/2,1);
        }
        int nl=l,nr=r;
        for(int i=0;i<26;i++)
        {
            if(a[i])
            {
                modify(i,1,nl,nl+a[i]/2-1,1);
                nl+=a[i]/2;
                modify(i,1,nr-a[i]/2+1,nr,1);
                nr-=a[i]/2;
            }
        }
    }
    
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<26;j++)
        {
            if(query(j,1,i,i))
            {
                printf("%c",j+'a');
                break;
            }
        }
    }
    return 0;
}

CF558E :https://www.luogu.com.cn/problem/CF558E

题目大意:每次询问给定一个区间和排序种类(升序或降序),最后输出所有操作结束后的字符串

思路:和上一题的思路差不多,用26颗线段树维护字符串,然后再根据排序需求从小到大或者从大到小遍历字符串进行填充。不同的是这道题不需要对称和分出现次数奇偶的情况,比上一题简单一些。

#define maxn 200010
int n,m;
char s[maxn];
struct node{
    int l,r;
    int sum,tag;
}seg[26][maxn<<2];
void pushup(int c,int rt)
{
    seg[c][rt].sum=seg[c][lson].sum+seg[c][rson].sum;
}
void build(int c,int rt,int l,int r)
{
    seg[c][rt].l=l;
    seg[c][rt].r=r;
    seg[c][rt].tag=-1;
    if(l==r)
    {
        seg[c][rt].sum=((s[l]-'a')==c);
        return ;
    }
    int mid = (l+r)>>1;
    build(c,lson,l,mid);
    build(c,rson,mid+1,r);
    pushup(c,rt);
}
void pushdown(int c,int rt)
{
    if(seg[c][rt].tag!=-1)
    {
        seg[c][lson].sum=(seg[c][lson].r-seg[c][lson].l+1)*seg[c][rt].tag;
        seg[c][rson].sum=(seg[c][rson].r-seg[c][rson].l+1)*seg[c][rt].tag;
        seg[c][lson].tag=seg[c][rt].tag;
        seg[c][rson].tag=seg[c][rt].tag;
        seg[c][rt].tag=-1;
    }
}
void modify(int c,int rt,int x,int y,int val)
{
    int l=seg[c][rt].l;
    int r=seg[c][rt].r;
    if(x<=l&&r<=y)
    {
        seg[c][rt].sum=(r-l+1)*val;
        seg[c][rt].tag=val;
        return ;
    }
    int mid = (l+r)>>1;
    pushdown(c,rt);
    if(x<=mid) modify(c,lson,x,y,val);
    if(y>mid) modify(c,rson,x,y,val);
    pushup(c,rt);
}
int query(int c,int rt,int x,int y)
{
    int l=seg[c][rt].l;
    int r=seg[c][rt].r;
    if(x<=l&&r<=y)
    {
        return seg[c][rt].sum;
    }
    int mid = (l+r)>>1;
    pushdown(c,rt);
    int ans=0;
    if(x<=mid) ans+=query(c,lson,x,y);
    if(y>mid) ans+=query(c,rson,x,y);
    return ans;
}
int main()
{
    // ios::sync_with_stdio(false);
    // cin.tie(0);
    // cout.tie(0);
    scanf("%d%d%s",&n,&m,s+1);
    for(int i=0;i<26;i++)
    {
        build(i,1,1,n);
    }
    while(m--)
    {
        int l,r,op;
        scanf("%d%d%d",&l,&r,&op);
        int a[26];
        for(int i=0;i<26;i++)
        {
            a[i]=query(i,1,l,r);
        }
        for(int i=0;i<26;i++)
        {
            modify(i,1,l,r,0);
        }
        int nl=l,nr=r;
        if(op==1)
        {
            for(int i=0;i<26;i++)
            {
                modify(i,1,nl,nl+a[i]-1,1);
                nl+=a[i];
            }
        }
        else 
        {
            for(int i=25;i>=0;i--)
            {
                modify(i,1,nl,nl+a[i]-1,1);
                nl+=a[i];
            }
        }
    }
    
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<26;j++)
        {
            if(query(j,1,i,i))
            {
                printf("%c",j+'a');
                break;
            }
        }
    }
    return 0;
}

标签:rt,CF240F,int,rson,CF558E,seg,tag,lson,例题
From: https://www.cnblogs.com/captainfly/p/18212442

相关文章

  • 状压dp 例题
    终于在洛谷上发布题解了QWQP10447最短Hamilton路径题解分析题目:一张nnn个点的带权无向图,求起点0......
  • Python案例题目,入门小白题
    1.抓取链家前十页的数据链家网址:长沙房产网_长沙房地产_长沙房产门户(长沙链家网)1.1.计算均价和总价importtime​fromseleniumimportwebdriverfromselenium.webdriver.common.byimportBy​driver=webdriver.Chrome()driver.get("https://cs.lianjia.com/zu......
  • 【每周例题】力扣 C++ 一年中的第几天
    一年中的第几天题目一年中的第几天 思路分析1.substr函数分割字符串,stoi函数将字符串转为十进制stoi函数介绍substr函数介绍2.判断是否为闰年,如果是闰年,则二月的天数+1代码#include<bits/stdc++.h>usingnamespacestd;intmain(){ intmonths[13]={0,31,28,3......
  • 【每周例题】判断回文串
    判断回文串题目给你一个字符串 x ,如果 x 是一个回文字符串,返回 true ;否则,返回 false 。回文字符串是指正序(从左向右)和倒序(从右向左)读都是一样的。例如,aba 是回文,而 abc 不是。代码#include<bits/stdc++.h>#include<cstring>usingnamespacestd;intmain(){......
  • 子类调用父类构造方法例题
    这段代码定义了三个类:Father(父类)、Child(子类)和Test(测试类)。首先,main方法执行newChild();时,会调用子类的构造函数。父类子类在子类Child的无参构造函数中,首先调用了this("dd"),这实际上是调用了Child类的有参构造函数但是,在子类Child的有参构造函数中,又调用了super("dd"),这......
  • 二分图(例题)
    https://www.cnblogs.com/kuangbiaopilihu/p/18184536$\quad$这里不再介绍二分图的基础知识,只是一些例题的解释。$\quad$当然,这道题可以用二分+并查集来解决。但这是二分图专辑,所以介绍一下二分图做法。$\quad$首先如果两个罪犯之间有仇恨,那么当他们不在同一......
  • 链表的查找操作例题
    代码/********************************************name:find*function:查找链表倒数第k位置的结点*argument:@head:头指针@k:链表倒数第k位置的结点数*retval:None*date:2024/04/22*note:Note*********************......
  • 顺序表的操作例题
    顺序表插入操作题目:已知一个顺序表L,其中的元素递增有序排列,设计一个算法,插入一个元素x(x为int型)后保持该顺序表仍然递增有序排列(假设插入操作总能成功)。代码/********************************************name:InsElem*function:递增有序排列插入一个元素x*ar......
  • 链表的操作例题
    链表的删除操作题目:设计一个算法删除单链表L(有头结点)中的一个最小值结点。/********************************************name:DelNode*function:删除单链表L中的一个最小值结点*argument:@L:单链表L的地址*retval:None*date:2024/04/22*n......
  • 【每周例题】力扣 C++ 分割字符串
    分割字符串题目 题目分析1.先确定用容器存储,容器的存储结构如下图所示: 2.这个题目的话,第一反应应该是用到动态规划,下面是动态规划的模板:res=[]ans=[]defbacktrack(未探索区域,res,path):if未探索区域满足结束条件:res.add(ans)#深度拷贝......